From 75f7ac20d05fc07bd23bbbaa37a5066b87ca87bb Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 10 Aug 2001 09:53:06 +0000 Subject: Initial merge of cvs-1.11 -> 1.11.1p1 changes onto mainline. There is still some suspicious and/or broken stuff to fix yet. --- contrib/cvs/src/client.c | 343 ++++++++++++-------- contrib/cvs/src/commit.c | 84 +++-- contrib/cvs/src/cvs.h | 56 ++-- contrib/cvs/src/diff.c | 116 ++++--- contrib/cvs/src/filesubr.c | 241 ++++++++++---- contrib/cvs/src/import.c | 28 +- contrib/cvs/src/lock.c | 22 +- contrib/cvs/src/login.c | 758 ++++++++++++++++++++++++++------------------ contrib/cvs/src/logmsg.c | 35 +- contrib/cvs/src/main.c | 221 +++++++------ contrib/cvs/src/mkmodules.c | 10 +- contrib/cvs/src/rcs.c | 246 +++----------- contrib/cvs/src/rcs.h | 11 +- contrib/cvs/src/rcscmds.c | 8 +- contrib/cvs/src/recurse.c | 24 +- contrib/cvs/src/server.c | 447 ++++++++++++++++---------- contrib/cvs/src/update.c | 131 +++++--- 17 files changed, 1580 insertions(+), 1201 deletions(-) (limited to 'contrib/cvs/src') diff --git a/contrib/cvs/src/client.c b/contrib/cvs/src/client.c index 525cb79..d166c60 100644 --- a/contrib/cvs/src/client.c +++ b/contrib/cvs/src/client.c @@ -36,6 +36,7 @@ # else /* No winsock.h */ # include # include +# include # include # endif /* No winsock.h */ #endif @@ -81,19 +82,7 @@ static Key_schedule sched; #ifdef HAVE_GSSAPI -#ifdef HAVE_GSSAPI_H -#include -#endif -#ifdef HAVE_GSSAPI_GSSAPI_H -#include -#endif -#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H -#include -#endif - -#ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE -#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name -#endif +# include "xgssapi.h" /* This is needed for GSSAPI encryption. */ static gss_ctx_id_t gcontext; @@ -101,7 +90,7 @@ static gss_ctx_id_t gcontext; static int connect_to_gserver PROTO((int, struct hostent *)); #endif /* HAVE_GSSAPI */ - + static void add_prune_candidate PROTO((char *)); /* All the commands. */ @@ -202,7 +191,7 @@ arg_should_not_be_sent_to_server (arg) 4) the argument lies within one of the paths in dirs_sent_to_server. - 4) */ + */ if (list_isempty (dirs_sent_to_server)) return 0; /* always send it */ @@ -267,8 +256,8 @@ arg_should_not_be_sent_to_server (arg) } /* Now check the value for root. */ - if (CVSroot_cmdline == NULL && this_root && current_root - && (strcmp (this_root, current_root) != 0)) + if (CVSroot_cmdline == NULL && this_root && current_parsed_root + && (strcmp (this_root, current_parsed_root->original) != 0)) { /* Don't send this, since the CVSROOTs don't match. */ free (this_root); @@ -1281,6 +1270,32 @@ warning: server is not creating directories one at a time"); if ( CVS_CHDIR (dir_name) < 0) error (1, errno, "could not chdir to %s", dir_name); } + else if (!isdir (CVSADM)) + { + /* + * Put repository in CVS/Repository. For historical + * (pre-CVS/Root) reasons, this is an absolute pathname, + * but what really matters is the part of it which is + * relative to cvsroot. + */ + char *repo; + + if (reposdirname_absolute) + repo = reposdirname; + else + { + repo = xmalloc (strlen (reposdirname) + + strlen (toplevel_repos) + + 10); + strcpy (repo, toplevel_repos); + strcat (repo, "/"); + strcat (repo, reposdirname); + } + + Create_Admin (".", ".", repo, (char *)NULL, (char *)NULL, 0, 1, 1); + if (repo != reposdirname) + free (repo); + } if (strcmp (command_name, "export") != 0) { @@ -2286,7 +2301,7 @@ static int is_cvsroot_level (pathname) char *pathname; { - if (strcmp (toplevel_repos, CVSroot_directory) != 0) + if (strcmp (toplevel_repos, current_parsed_root->directory) != 0) return 0; return strchr (pathname, '/') == NULL; @@ -2926,14 +2941,14 @@ send_a_repository (dir, repository, update_dir) from REPOSITORY. If the path elements don't exist in REPOSITORY, or the removal of those path elements mean that we "step above" - CVSroot_directory, set toplevel_repos to - CVSroot_directory. */ + current_parsed_root->directory, set toplevel_repos to + current_parsed_root->directory. */ if ((repository_len > update_dir_len) && (strcmp (repository + repository_len - update_dir_len, update_dir) == 0) - /* TOPLEVEL_REPOS shouldn't be above CVSroot_directory */ + /* TOPLEVEL_REPOS shouldn't be above current_parsed_root->directory */ && ((repository_len - update_dir_len) - > strlen (CVSroot_directory))) + > strlen (current_parsed_root->directory))) { /* The repository name contains UPDATE_DIR. Set toplevel_repos to the repository name without @@ -2947,7 +2962,7 @@ send_a_repository (dir, repository, update_dir) } else { - toplevel_repos = xstrdup (CVSroot_directory); + toplevel_repos = xstrdup (current_parsed_root->directory); } } } @@ -3005,7 +3020,7 @@ client_expand_modules (argc, argv, local) for (i = 0; i < argc; ++i) send_arg (argv[i]); - send_a_repository ("", CVSroot_directory, ""); + send_a_repository ("", current_parsed_root->directory, ""); send_to_server ("expand-modules\012", 0); @@ -3043,13 +3058,13 @@ client_send_expansions (local, where, build_dirs) if (isfile (argv[0])) send_files (1, argv, local, 0, build_dirs ? SEND_BUILD_DIRS : 0); } - send_a_repository ("", CVSroot_directory, ""); + send_a_repository ("", current_parsed_root->directory, ""); } void client_nonexpanded_setup () { - send_a_repository ("", CVSroot_directory, ""); + send_a_repository ("", current_parsed_root->directory, ""); } /* Receive a cvswrappers line from the server; it must be a line @@ -3564,7 +3579,8 @@ get_responses_and_close () { if (shutdown (server_fd, 1) < 0) error (1, 0, "shutting down connection to %s: %s", - CVSroot_hostname, SOCK_STRERROR (SOCK_ERRNO)); + current_parsed_root->hostname, SOCK_STRERROR (SOCK_ERRNO)); + server_fd = -1; /* * This test will always be true because we dup the descriptor */ @@ -3573,7 +3589,7 @@ get_responses_and_close () if (fclose (to_server_fp) != 0) error (1, errno, "closing down connection to %s", - CVSroot_hostname); + current_parsed_root->hostname); } } else @@ -3591,15 +3607,15 @@ get_responses_and_close () #endif /* START_RSH_WITH_POPEN_RW */ { error (1, errno, "closing connection to %s", - CVSroot_hostname); + current_parsed_root->hostname); } } if (! buf_empty_p (from_server) || getc (from_server_fp) != EOF) - error (0, 0, "dying gasps from %s unexpected", CVSroot_hostname); + error (0, 0, "dying gasps from %s unexpected", current_parsed_root->hostname); else if (ferror (from_server_fp)) - error (0, errno, "reading from %s", CVSroot_hostname); + error (0, errno, "reading from %s", current_parsed_root->hostname); fclose (from_server_fp); #endif /* SHUTDOWN_SERVER */ @@ -3616,8 +3632,7 @@ get_responses_and_close () /* see if we need to sleep before returning to avoid time-stamp races */ if (last_register_time) { - while (time ((time_t *) NULL) == last_register_time) - sleep (1); + sleep_past (last_register_time); } return errs; @@ -3641,7 +3656,8 @@ supported_request (name) return 0; } - + + #if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_KERBEROS) static struct hostent *init_sockaddr PROTO ((struct sockaddr_in *, char *, unsigned int)); @@ -3670,22 +3686,86 @@ init_sockaddr (name, hostname, port) #endif /* defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_KERBEROS) */ -#ifdef AUTH_CLIENT_SUPPORT -static int auth_server_port_number PROTO ((void)); +#ifdef AUTH_CLIENT_SUPPORT + +/* Generic function to do port number lookup tasks. + * + * In order of precedence, will return: + * getenv (envname), if defined + * getservbyname (portname), if defined + * defaultport + */ static int -auth_server_port_number () +get_port_number (envname, portname, defaultport) + const char *envname; + const char *portname; + int defaultport; { - struct servent *s = getservbyname ("cvspserver", "tcp"); + struct servent *s; + char *port_s; - if (s) + if (envname && (port_s = getenv (envname))) + { + int port = atoi (port_s); + if (port <= 0) + { + error (0, 0, "%s must be a positive integer! If you", envname); + error (0, 0, "are trying to force a connection via rsh, please"); + error (0, 0, "put \":server:\" at the beginning of your CVSROOT"); + error (1, 0, "variable."); + } + return port; + } + else if (portname && (s = getservbyname (portname, "tcp"))) return ntohs (s->s_port); else - return CVS_AUTH_PORT; + return defaultport; +} + + + +/* get the port number for a client to connect to based on the port + * and method of a cvsroot_t. + * + * we do this here instead of in parse_cvsroot so that we can keep network + * code confined to a localized area and also to delay the lookup until the + * last possible moment so it remains possible to run cvs client commands that + * skip opening connections to the server (i.e. skip network operations entirely) + * + * and yes, I know none of the the commands do that now, but here's to planning + * for the future, eh? cheers. + * + * FIXME - We could cache the port lookup safely right now as we never change + * it for a single root on the fly, but we'd have to un'const some other + * functions + */ +int +get_cvs_port_number (root) + const cvsroot_t *root; +{ + + if (root->port) return root->port; + + switch (root->method) + { + case gserver_method: + case pserver_method: + return get_port_number ("CVS_CLIENT_PORT", "cvspserver", CVS_AUTH_PORT); +#ifdef HAVE_KERBEROS + case kserver_method: + return get_port_number ("CVS_CLIENT_PORT", "cvs", CVS_PORT); +#endif + default: + error(1, EINVAL, "internal error: get_cvs_port_number called for invalid connection method (%s)", + method_names[root->method]); + break; + } } + /* Read a line from socket SOCK. Result does not include the terminating linefeed. This is only used by the authentication protocol, which we call before we set up all the buffering stuff. @@ -3713,7 +3793,7 @@ recv_line (sock, resultp) int n; n = recv (sock, &ch, 1, 0); if (n <= 0) - error (1, 0, "recv() from server %s: %s", CVSroot_hostname, + error (1, 0, "recv() from server %s: %s", current_parsed_root->hostname, n == 0 ? "EOF" : SOCK_STRERROR (SOCK_ERRNO)); if (ch == '\012') @@ -3751,11 +3831,15 @@ connect_to_forked_server (tofdp, fromfdp) command[0] = getenv ("CVS_SERVER"); if (! command[0]) - command[0] = "cvs"; + command[0] = program_path; command[1] = "server"; command[2] = NULL; + if (trace) + { + fprintf (stderr, " -> Forking server: %s %s\n", command[0], command[1]); + } if (! piped_child (command, tofdp, fromfdp)) error (1, 0, "could not fork server process"); } @@ -3783,20 +3867,29 @@ connect_to_pserver (tofdp, fromfdp, verify_only, do_gssapi) int tofd, fromfd; #endif int port_number; + char *username; /* the username we use to connect */ struct sockaddr_in client_sai; struct hostent *hostinfo; - char no_passwd = 0; /* gets set if no password found */ + char no_passwd = 0; /* gets set if no password found */ sock = socket (AF_INET, SOCK_STREAM, 0); if (sock == -1) { error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO)); } - port_number = auth_server_port_number (); - hostinfo = init_sockaddr (&client_sai, CVSroot_hostname, port_number); + port_number = get_cvs_port_number (current_parsed_root); + hostinfo = init_sockaddr (&client_sai, current_parsed_root->hostname, port_number); + if (trace) + { + fprintf (stderr, " -> Connecting to %s(%s):%d\n", + current_parsed_root->hostname, + inet_ntoa (client_sai.sin_addr), port_number); + } if (connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai)) < 0) - error (1, 0, "connect to %s:%d failed: %s", CVSroot_hostname, + error (1, 0, "connect to %s(%s):%d failed: %s", + current_parsed_root->hostname, + inet_ntoa (client_sai.sin_addr), port_number, SOCK_STRERROR (SOCK_ERRNO)); /* Run the authorization mini-protocol before anything else. */ @@ -3804,7 +3897,12 @@ connect_to_pserver (tofdp, fromfdp, verify_only, do_gssapi) { #ifdef HAVE_GSSAPI if (! connect_to_gserver (sock, hostinfo)) + { + error (0, 0, + "authorization failed: server %s rejected access to %s", + current_parsed_root->hostname, current_parsed_root->directory); goto rejected; + } #else error (1, 0, "This client does not support GSSAPI authentication"); #endif @@ -3812,11 +3910,9 @@ connect_to_pserver (tofdp, fromfdp, verify_only, do_gssapi) else { char *begin = NULL; - char *repository = CVSroot_directory; - char *username = CVSroot_username; char *password = NULL; char *end = NULL; - + if (verify_only) { begin = "BEGIN VERIFICATION REQUEST\012"; @@ -3830,7 +3926,8 @@ connect_to_pserver (tofdp, fromfdp, verify_only, do_gssapi) /* Get the password, probably from ~/.cvspass. */ password = get_cvs_password (); - + username = current_parsed_root->username ? current_parsed_root->username : getcaller(); + /* Send the empty string by default. This is so anonymous CVS access doesn't require client to have done "cvs login". */ if (password == NULL) @@ -3844,7 +3941,7 @@ connect_to_pserver (tofdp, fromfdp, verify_only, do_gssapi) error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO)); /* Send the data the server needs. */ - if (send (sock, repository, strlen (repository), 0) < 0) + if (send (sock, current_parsed_root->directory, strlen (current_parsed_root->directory), 0) < 0) error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO)); if (send (sock, "\012", 1, 0) < 0) error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO)); @@ -3875,7 +3972,29 @@ connect_to_pserver (tofdp, fromfdp, verify_only, do_gssapi) if (strcmp (read_buf, "I HATE YOU") == 0) { - /* Authorization not granted. */ + /* Authorization not granted. + * + * This is a little confusing since we can reach this while loop in GSSAPI + * mode, but if GSSAPI authentication failed, we already jumped to the + * rejected label (there is no case where the connect_to_gserver function + * can return 1 and we will not receive "I LOVE YOU" from the server, barring + * broken connections and garbled messages, of course). + * + * i.e. This is a pserver specific error message and shoiuld be since + * GSSAPI doesn't use username. + */ + error (0, 0, + "authorization failed: server %s rejected access to %s for user %s", + current_parsed_root->hostname, current_parsed_root->directory, username); + + /* Output a special error message if authentication was attempted + with no password -- the user should be made aware that they may + have missed a step. */ + if (no_passwd) + { + error (0, 0, + "used empty password; try \"cvs login\" with a real password"); + } goto rejected; } else if (strncmp (read_buf, "E ", 2) == 0) @@ -3913,15 +4032,15 @@ connect_to_pserver (tofdp, fromfdp, verify_only, do_gssapi) { error (0, 0, "unrecognized auth response from %s: %s", - CVSroot_hostname, read_buf); + current_parsed_root->hostname, read_buf); error (1, 0, "shutdown() failed, server %s: %s", - CVSroot_hostname, + current_parsed_root->hostname, SOCK_STRERROR (SOCK_ERRNO)); } error (1, 0, "unrecognized auth response from %s: %s", - CVSroot_hostname, read_buf); + current_parsed_root->hostname, read_buf); } free (read_buf); } @@ -3930,7 +4049,7 @@ connect_to_pserver (tofdp, fromfdp, verify_only, do_gssapi) if (verify_only) { if (shutdown (sock, 2) < 0) - error (0, 0, "shutdown() failed, server %s: %s", CVSroot_hostname, + error (0, 0, "shutdown() failed, server %s: %s", current_parsed_root->hostname, SOCK_STRERROR (SOCK_ERRNO)); return; } @@ -3955,24 +4074,11 @@ connect_to_pserver (tofdp, fromfdp, verify_only, do_gssapi) return; rejected: - error (0, 0, - "authorization failed: server %s rejected access to %s for user %s", - CVSroot_hostname, CVSroot_directory, CVSroot_username); - - /* Output a special error message if authentication was attempted - with no password -- the user should be made aware that they may - have missed a step. */ - if (no_passwd) - { - error (0, 0, - "used empty password; try \"cvs login\" with a real password"); - } - if (shutdown (sock, 2) < 0) { error (0, 0, "shutdown() failed (server %s): %s", - CVSroot_hostname, + current_parsed_root->hostname, SOCK_STRERROR (SOCK_ERRNO)); } @@ -3980,8 +4086,9 @@ connect_to_pserver (tofdp, fromfdp, verify_only, do_gssapi) } #endif /* AUTH_CLIENT_SUPPORT */ - -#if HAVE_KERBEROS + + +#ifdef HAVE_KERBEROS /* This function has not been changed to deal with NO_SOCKET_TO_FD (i.e., systems on which sockets cannot be converted to file @@ -4003,42 +4110,26 @@ start_tcp_server (tofdp, fromfdp) if (s < 0) error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO)); - /* Get CVS_CLIENT_PORT or look up cvs/tcp with CVS_PORT as default */ - portenv = getenv ("CVS_CLIENT_PORT"); - if (portenv != NULL) - { - port = atoi (portenv); - if (port <= 0) - { - error (0, 0, "CVS_CLIENT_PORT must be a positive number! If you"); - error (0, 0, "are trying to force a connection via rsh, please"); - error (0, 0, "put \":server:\" at the beginning of your CVSROOT"); - error (1, 0, "variable."); - } - if (trace) - fprintf(stderr, "Using TCP port %d to contact server.\n", port); - } - else - { - struct servent *sp; - - sp = getservbyname ("cvs", "tcp"); - if (sp == NULL) - port = CVS_PORT; - else - port = ntohs (sp->s_port); - } + port = get_cvs_port_number (current_parsed_root); - hp = init_sockaddr (&sin, CVSroot_hostname, port); + hp = init_sockaddr (&sin, current_parsed_root->hostname, port); hname = xmalloc (strlen (hp->h_name) + 1); strcpy (hname, hp->h_name); + if (trace) + { + fprintf (stderr, " -> Connecting to %s(%s):%d\n", + current_parsed_root->hostname, + inet_ntoa (client_sai.sin_addr), port); + } + if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0) - error (1, 0, "connect to %s:%d failed: %s", CVSroot_hostname, + error (1, 0, "connect to %s(%s):%d failed: %s", + current_parsed_root->hostname, + inet_ntoa (client_sai.sin_addr), port, SOCK_STRERROR (SOCK_ERRNO)); -#ifdef HAVE_KERBEROS { const char *realm; struct sockaddr_in laddr; @@ -4063,7 +4154,6 @@ start_tcp_server (tofdp, fromfdp) krb_get_err_text (status)); memcpy (kblock, cred.session, sizeof (C_Block)); } -#endif /* HAVE_KERBEROS */ server_fd = s; close_on_exec (server_fd); @@ -4092,9 +4182,10 @@ recv_bytes (sock, buf, need) int got; got = recv (sock, buf, need, 0); - if (got < 0) - error (1, 0, "recv() from server %s: %s", CVSroot_hostname, - SOCK_STRERROR (SOCK_ERRNO)); + if (got <= 0) + error (1, 0, "recv() from server %s: %s", current_parsed_root->hostname, + got == 0 ? "EOF" : SOCK_STRERROR (SOCK_ERRNO)); + buf += got; need -= got; } @@ -4187,11 +4278,11 @@ connect_to_gserver (sock, hostinfo) got = recv (sock, buf + 2, sizeof buf - 2, 0); if (got < 0) error (1, 0, "recv() from server %s: %s", - CVSroot_hostname, SOCK_STRERROR (SOCK_ERRNO)); + current_parsed_root->hostname, SOCK_STRERROR (SOCK_ERRNO)); buf[got + 2] = '\0'; if (buf[got + 1] == '\n') buf[got + 1] = '\0'; - error (1, 0, "error from server %s: %s", CVSroot_hostname, + error (1, 0, "error from server %s: %s", current_parsed_root->hostname, buf); } @@ -4243,7 +4334,7 @@ start_server () (*really* slow on a 14.4kbps link); the clean way to have a CVS which supports several ways of connecting is with access methods. */ - switch (CVSroot_method) + switch (current_parsed_root->method) { #ifdef AUTH_CLIENT_SUPPORT @@ -4260,7 +4351,7 @@ start_server () break; #endif -#if HAVE_GSSAPI +#ifdef HAVE_GSSAPI case gserver_method: /* GSSAPI authentication is handled by the pserver. */ connect_to_pserver (&tofd, &fromfd, 0, 1); @@ -4279,8 +4370,8 @@ start_server () case server_method: #if defined(START_SERVER) START_SERVER (&tofd, &fromfd, getcaller (), - CVSroot_username, CVSroot_hostname, - CVSroot_directory); + current_parsed_root->username, current_parsed_root->hostname, + current_parsed_root->directory); # if defined (START_SERVER_RETURNS_SOCKET) && defined (NO_SOCKET_TO_FD) /* This is a system on which we can only write to a socket using send/recv. Therefore its START_SERVER needs to @@ -4411,7 +4502,7 @@ the :server: access method is not supported by this port of CVS"); if (!rootless) { send_to_server ("Root ", 0); - send_to_server (CVSroot_directory, 0); + send_to_server (current_parsed_root->directory, 0); send_to_server ("\012", 1); } @@ -4541,7 +4632,7 @@ the :server: access method is not supported by this port of CVS"); on encryption, bomb out; don't let the user think the data is being encrypted when it is not. */ #ifdef HAVE_KERBEROS - if (CVSroot_method == kserver_method) + if (current_parsed_root->method == kserver_method) { if (! supported_request ("Kerberos-encrypt")) error (1, 0, "This server does not support encryption"); @@ -4556,7 +4647,7 @@ the :server: access method is not supported by this port of CVS"); else #endif /* HAVE_KERBEROS */ #ifdef HAVE_GSSAPI - if (CVSroot_method == gserver_method) + if (current_parsed_root->method == gserver_method) { if (! supported_request ("Gssapi-encrypt")) error (1, 0, "This server does not support encryption"); @@ -4629,7 +4720,7 @@ the :server: access method is not supported by this port of CVS"); ability to decrypt the data stream is itself a form of authentication. */ #ifdef HAVE_GSSAPI - if (CVSroot_method == gserver_method) + if (current_parsed_root->method == gserver_method) { if (! supported_request ("Gssapi-authenticate")) error (1, 0, @@ -4733,13 +4824,13 @@ start_rsh_server (tofdp, fromfdp) #endif /* RSH_NEEDS_BINARY_FLAG */ /* Then we strcat more things on the end one by one. */ - if (CVSroot_username != NULL) + if (current_parsed_root->username != NULL) { rsh_argv[i++] = "-l"; - rsh_argv[i++] = CVSroot_username; + rsh_argv[i++] = current_parsed_root->username; } - rsh_argv[i++] = CVSroot_hostname; + rsh_argv[i++] = current_parsed_root->hostname; rsh_argv[i++] = cvs_server; rsh_argv[i++] = "server"; @@ -4749,6 +4840,8 @@ start_rsh_server (tofdp, fromfdp) if (trace) { fprintf (stderr, " -> Starting server: "); + for (i = 0; rsh_argv[i]; i++) + fprintf (stderr, "%s ", rsh_argv[i]); putc ('\n', stderr); } @@ -4786,7 +4879,7 @@ start_rsh_server (tofdp, fromfdp) versions of rsh that grab switches out of the middle of the command (they're calling the GNU getopt routines incorrectly). */ command = xmalloc (strlen (cvs_server) - + strlen (CVSroot_directory) + + strlen (current_parsed_root->directory) + 50); /* If you are running a very old (Nov 3, 1994, before 1.5) @@ -4800,15 +4893,15 @@ start_rsh_server (tofdp, fromfdp) char **p = argv; *p++ = cvs_rsh; - *p++ = CVSroot_hostname; + *p++ = current_parsed_root->hostname; /* If the login names differ between client and server * pass it on to rsh. */ - if (CVSroot_username != NULL) + if (current_parsed_root->username != NULL) { *p++ = "-l"; - *p++ = CVSroot_username; + *p++ = current_parsed_root->username; } *p++ = command; @@ -5535,7 +5628,7 @@ send_files (argc, argv, local, aflag, flags) * latter case; I don't think toplevel_repos matters for the * former. */ - toplevel_repos = xstrdup (CVSroot_directory); + toplevel_repos = xstrdup (current_parsed_root->directory); send_repository ("", toplevel_repos, "."); } @@ -5654,7 +5747,7 @@ client_import_done () */ /* FIXME: "can't happen" now that we call client_import_setup at the beginning. */ - toplevel_repos = xstrdup (CVSroot_directory); + toplevel_repos = xstrdup (current_parsed_root->directory); send_repository ("", toplevel_repos, "."); } @@ -5834,9 +5927,9 @@ client_senddate (date) void send_init_command () { - /* This is here because we need the CVSroot_directory variable. */ + /* This is here because we need the current_parsed_root->directory variable. */ send_to_server ("init ", 0); - send_to_server (CVSroot_directory, 0); + send_to_server (current_parsed_root->directory, 0); send_to_server ("\012", 0); } diff --git a/contrib/cvs/src/commit.c b/contrib/cvs/src/commit.c index 3d46e25..226b09b 100644 --- a/contrib/cvs/src/commit.c +++ b/contrib/cvs/src/commit.c @@ -86,13 +86,13 @@ static time_t last_register_time; static const char *const commit_usage[] = { "Usage: %s %s [-nRlf] [-m msg | -F logfile] [-r rev] files...\n", - "\t-n\tDo not run the module program (if any).\n", - "\t-R\tProcess directories recursively.\n", - "\t-l\tLocal directory only (not recursive).\n", - "\t-f\tForce the file to be committed; disables recursion.\n", - "\t-F file\tRead the log message from file.\n", - "\t-m msg\tLog message.\n", - "\t-r rev\tCommit to this branch or trunk revision.\n", + " -n Do not run the module program (if any).\n", + " -R Process directories recursively.\n", + " -l Local directory only (not recursive).\n", + " -f Force the file to be committed; disables recursion.\n", + " -F logfile Read the log message from file.\n", + " -m msg Log message.\n", + " -r rev Commit to this branch or trunk revision.\n", "(Specify the --help global option for a list of other help options)\n", NULL }; @@ -345,7 +345,7 @@ commit (argc, argv) if (geteuid () == (uid_t) 0 # ifdef CLIENT_SUPPORT /* Who we are on the client side doesn't affect logging. */ - && !client_active + && !current_parsed_root->isremote # endif ) { @@ -433,7 +433,7 @@ commit (argc, argv) } #ifdef CLIENT_SUPPORT - if (client_active) + if (current_parsed_root->isremote) { struct find_data find_args; @@ -592,8 +592,7 @@ commit (argc, argv) char *fname; FILE *fp; - fname = cvs_temp_name (); - fp = CVS_FOPEN (fname, "w+"); + fp = cvs_temp_file (&fname); if (fp == NULL) error (1, 0, "cannot create temporary file %s", fname); if (fwrite (saved_message, 1, strlen (saved_message), fp) @@ -602,6 +601,7 @@ commit (argc, argv) if (fclose (fp) < 0) error (0, errno, "cannot close temporary file %s", fname); error (0, 0, "saving log message in %s", fname); + free (fname); } return err; } @@ -616,7 +616,7 @@ commit (argc, argv) wrap_setup (); - lock_tree_for_write (argc, argv, local, aflag); + lock_tree_for_write (argc, argv, local, W_LOCAL, aflag); /* * Set up the master update list and hard link list @@ -664,11 +664,15 @@ commit (argc, argv) Lock_Cleanup (); dellist (&mulist); +#ifdef SERVER_SUPPORT + if (server_active) + return err; +#endif + /* see if we need to sleep before returning to avoid time-stamp races */ if (last_register_time) { - while (time ((time_t *) NULL) == last_register_time) - sleep (1); + sleep_past (last_register_time); } return (err); @@ -779,7 +783,7 @@ check_fileproc (callerdat, finfo) struct commit_info *ci; struct logfile_info *li; - size_t cvsroot_len = strlen (CVSroot_directory); + size_t cvsroot_len = strlen (current_parsed_root->directory); if (!finfo->repository) { @@ -787,7 +791,7 @@ check_fileproc (callerdat, finfo) return (1); } - if (strncmp (finfo->repository, CVSroot_directory, cvsroot_len) == 0 + if (strncmp (finfo->repository, current_parsed_root->directory, cvsroot_len) == 0 && ISDIRSEP (finfo->repository[cvsroot_len]) && strncmp (finfo->repository + cvsroot_len + 1, CVSROOTADM, @@ -811,9 +815,7 @@ check_fileproc (callerdat, finfo) switch (status) { case T_CHECKOUT: -#ifdef SERVER_SUPPORT case T_PATCH: -#endif case T_NEEDS_MERGE: case T_CONFLICT: case T_REMOVE_ENTRY: @@ -830,7 +832,7 @@ check_fileproc (callerdat, finfo) * Also, * - if status is T_REMOVED, can't have a numeric tag * - if status is T_ADDED, rcs file must not exist unless on - * a branch + * a branch or head is dead * - if status is T_ADDED, can't have a non-trunk numeric rev * - if status is T_MODIFIED and a Conflict marker exists, don't * allow the commit if timestamp is identical or if we find @@ -927,29 +929,17 @@ warning: file `%s' seems to still contain conflict indicators", { if (vers->tag == NULL) { - char *rcs; - - rcs = xmalloc (strlen (finfo->repository) - + strlen (finfo->file) - + sizeof RCSEXT - + 5); - - /* Don't look in the attic; if it exists there we - will move it back out in checkaddfile. */ - sprintf(rcs, "%s/%s%s", finfo->repository, finfo->file, - RCSEXT); - if (isreadable (rcs)) + if (finfo->rcs != NULL && + !RCS_isdead (finfo->rcs, finfo->rcs->head)) { error (0, 0, "cannot add file `%s' when RCS file `%s' already exists", - finfo->fullname, rcs); + finfo->fullname, finfo->rcs->path); freevers_ts (&vers); - free (rcs); return (1); } - free (rcs); } - if (vers->tag && isdigit ((unsigned char) *vers->tag) && + else if (isdigit ((unsigned char) *vers->tag) && numdots (vers->tag) > 1) { error (0, 0, @@ -1312,6 +1302,12 @@ commit_fileproc (callerdat, finfo) /* find the max major rev number in this directory */ maxrev = 0; (void) walklist (finfo->entries, findmaxrev, NULL); + if (finfo->rcs->head) { + /* resurrecting: include dead revision */ + int thisrev = atoi (finfo->rcs->head); + if (thisrev > maxrev) + maxrev = thisrev; + } if (maxrev == 0) maxrev = 1; xrev = xmalloc (20); @@ -1431,12 +1427,12 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries) { char *p; - if (strncmp (CVSroot_directory, repository, - strlen (CVSroot_directory)) != 0) + if (strncmp (current_parsed_root->directory, repository, + strlen (current_parsed_root->directory)) != 0) error (0, 0, "internal error: repository (%s) doesn't begin with root (%s)", - repository, CVSroot_directory); - p = repository + strlen (CVSroot_directory); + repository, current_parsed_root->directory); + p = repository + strlen (current_parsed_root->directory); if (*p == '/') ++p; if (strcmp ("CVSROOT", p) == 0 @@ -1597,19 +1593,13 @@ findmaxrev (p, closure) Node *p; void *closure; { - char *cp; int thisrev; Entnode *entdata; entdata = (Entnode *) p->data; if (entdata->type != ENT_FILE) return (0); - cp = strchr (entdata->version, '.'); - if (cp != NULL) - *cp = '\0'; thisrev = atoi (entdata->version); - if (cp != NULL) - *cp = '.'; if (thisrev > maxrev) maxrev = thisrev; return (0); @@ -1957,10 +1947,8 @@ checkaddfile (file, repository, tag, options, rcsnode) Attic. */ if (!(rcsfile->flags & INATTIC)) { - error (0, 0, "internal error: confused about attic for %s", + error (0, 0, "warning: expected %s to be in Attic", rcsfile->path); - retval = 1; - goto out; } sprintf (rcs, "%s/%s%s", repository, file, RCSEXT); diff --git a/contrib/cvs/src/cvs.h b/contrib/cvs/src/cvs.h index 6489f07..8ce1cef 100644 --- a/contrib/cvs/src/cvs.h +++ b/contrib/cvs/src/cvs.h @@ -318,7 +318,7 @@ typedef struct entnode Entnode; /* The type of request that is being done in do_module() */ enum mtype { - CHECKOUT, TAG, PATCH, EXPORT + CHECKOUT, TAG, PATCH, EXPORT, MISC }; /* @@ -371,28 +371,34 @@ extern char *RCS_citag; /* Access method specified in CVSroot. */ typedef enum { - local_method, server_method, pserver_method, kserver_method, gserver_method, + null_method, local_method, server_method, pserver_method, kserver_method, gserver_method, ext_method, fork_method } CVSmethod; extern char *method_names[]; /* change this in root.c if you change the enum above */ +typedef struct cvsroot_s { + char *original; /* the complete source CVSroot string */ + CVSmethod method; /* one of the enum values above */ + char *username; /* the username or NULL if method == local */ + char *password; /* the username or NULL if method == local */ + char *hostname; /* the hostname or NULL if method == local */ + int port; /* the port or zero if method == local */ + char *directory; /* the directory name */ +#ifdef CLIENT_SUPPORT + unsigned char isremote; /* nonzero if we are doing remote access */ +#endif /* CLIENT_SUPPORT */ +} cvsroot_t; + /* This global variable holds the global -d option. It is NULL if -d was not used, which means that we must get the CVSroot information from the CVSROOT environment variable or from a CVS/Root file. */ extern char *CVSroot_cmdline; -extern char *CVSroot_original; /* the active, complete CVSroot string */ -extern int client_active; /* nonzero if we are doing remote access */ -extern CVSmethod CVSroot_method; /* one of the enum values above */ -extern char *CVSroot_username; /* the username or NULL if method == local */ -extern char *CVSroot_hostname; /* the hostname or NULL if method == local */ -extern char *CVSroot_directory; /* the directory name */ - /* These variables keep track of all of the CVSROOT directories that have been seen by the client and the current one of those selected. */ extern List *root_directories; -extern char *current_root; +extern cvsroot_t *current_parsed_root; extern char *emptydir_name PROTO ((void)); extern int safe_location PROTO ((void)); @@ -429,8 +435,9 @@ extern int RCS_exec_rcsdiff PROTO ((RCSNode *rcsfile, char *rev1, char *rev2, char *label1, char *label2, char *workfile)); -extern int diff_exec PROTO ((char *file1, char *file2, char *options, - char *out)); +extern int diff_exec PROTO ((char *file1, char *file2, + char *label1, char *label2, + char *options, char *out)); extern int diff_execv PROTO ((char *file1, char *file2, char *label1, char *label2, char *options, char *out)); @@ -450,15 +457,18 @@ void Subdir_Deregister PROTO((List *, const char *, const char *)); char *Make_Date PROTO((char *rawdate)); char *date_from_time_t PROTO ((time_t)); -void date_to_internet PROTO ((char *, char *)); +void date_to_internet PROTO ((char *, const char *)); +void date_to_tm PROTO ((struct tm *, const char *)); +void tm_to_internet PROTO ((char *, const struct tm *)); char *Name_Repository PROTO((char *dir, char *update_dir)); char *Short_Repository PROTO((char *repository)); void Sanitize_Repository_Name PROTO((char *repository)); char *Name_Root PROTO((char *dir, char *update_dir)); -int parse_cvsroot PROTO((char *CVSroot)); -void set_local_cvsroot PROTO((char *dir)); +void free_cvsroot_t PROTO((cvsroot_t *root_in)); +cvsroot_t *parse_cvsroot PROTO((char *root)); +cvsroot_t *local_cvsroot PROTO((char *dir)); void Create_Root PROTO((char *dir, char *rootdir)); void root_allow_add PROTO ((char *)); void root_allow_free PROTO ((void)); @@ -472,6 +482,7 @@ char *time_stamp PROTO((char *file)); void *xmalloc PROTO((size_t bytes)); void *xrealloc PROTO((void *ptr, size_t bytes)); void expand_string PROTO ((char **, size_t *, size_t)); +void allocate_and_strcat PROTO ((char **, size_t *, const char *)); char *xstrdup PROTO((const char *str)); void strip_trailing_newlines PROTO((char *str)); int pathname_levels PROTO ((char *path)); @@ -494,6 +505,7 @@ char *xreadlink PROTO((const char *link)); char *last_component PROTO((char *path)); char *get_homedir PROTO ((void)); char *cvs_temp_name PROTO ((void)); +FILE *cvs_temp_file PROTO ((char **filename)); void parseopts PROTO ((const char *root)); int numdots PROTO((const char *s)); @@ -518,7 +530,8 @@ void Lock_Cleanup PROTO((void)); /* Writelock an entire subtree, well the part specified by ARGC, ARGV, LOCAL, and AFLAG, anyway. */ -void lock_tree_for_write PROTO ((int argc, char **argv, int local, int aflag)); +void lock_tree_for_write PROTO ((int argc, char **argv, int local, int which, + int aflag)); /* See lock.c for description. */ extern void lock_dir_for_write PROTO ((char *)); @@ -555,7 +568,6 @@ void make_directories PROTO((const char *name)); void make_directory PROTO((const char *name)); extern int mkdir_if_needed PROTO ((char *name)); void rename_file PROTO((const char *from, const char *to)); -char *backup_file PROTO((const char *file, const char *suffix)); /* Expand wildcards in each element of (ARGC,ARGV). This is according to the files which exist in the current directory, and accordingly to OS-specific conventions regarding wildcard syntax. It might be desirable to change the @@ -635,7 +647,8 @@ extern int init PROTO ((int argc, char **argv)); int do_module PROTO((DBM * db, char *mname, enum mtype m_type, char *msg, CALLBACKPROC callback_proc, char *where, int shorten, - int local_specified, int run_module_prog, char *extra_arg)); + int local_specified, int run_module_prog, int build_dirs, + char *extra_arg)); void history_write PROTO((int type, char *update_dir, char *revs, char *name, char *repository)); int start_recursion PROTO((FILEPROC fileproc, FILESDONEPROC filesdoneproc, @@ -653,7 +666,10 @@ char *make_message_rcslegal PROTO((char *message)); extern int file_has_markers PROTO ((const struct file_info *)); extern void get_file PROTO ((const char *, const char *, const char *, char **, size_t *, size_t *)); +extern char *shell_escape PROTO((char *buf, const char *str)); +char *backup_file PROTO((const char *file, const char *suffix)); extern void resolve_symlink PROTO ((char **filename)); +void sleep_past PROTO ((time_t desttime)); /* flags for run_exec(), the fast system() for CVS */ #define RUN_NORMAL 0x0000 /* no special behaviour */ @@ -783,9 +799,7 @@ enum classify_type T_REMOVED, /* R (removed file) list */ T_REMOVE_ENTRY, /* W (removed entry) list */ T_UPTODATE, /* File is up-to-date */ -#ifdef SERVER_SUPPORT T_PATCH, /* P Like C, but can patch */ -#endif T_TITLE /* title for node type */ }; typedef enum classify_type Ctype; @@ -875,6 +889,8 @@ char *descramble PROTO ((char *str)); #ifdef AUTH_CLIENT_SUPPORT char *get_cvs_password PROTO((void)); +int get_cvs_port_number PROTO((const cvsroot_t *root)); +char *normalize_cvsroot PROTO((const cvsroot_t *root)); #endif /* AUTH_CLIENT_SUPPORT */ extern void tag_check_valid PROTO ((char *, int, char **, int, int, char *)); diff --git a/contrib/cvs/src/diff.c b/contrib/cvs/src/diff.c index 15353e63..f24101b 100644 --- a/contrib/cvs/src/diff.c +++ b/contrib/cvs/src/diff.c @@ -119,7 +119,7 @@ static struct option const longopts[] = {"ignore-matching-lines", 1, 0, 'I'}, {"label", 1, 0, 'L'}, {"new-file", 0, 0, 'N'}, - {"initial-tab", 0, 0, 148}, + {"initial-tab", 0, 0, 'T'}, {"width", 1, 0, 'W'}, {"text", 0, 0, 'a'}, {"ignore-space-change", 0, 0, 'b'}, @@ -140,7 +140,7 @@ static struct option const longopts[] = {"report-identical-files", 0, 0, 's'}, {"expand-tabs", 0, 0, 't'}, {"ignore-all-space", 0, 0, 'w'}, - {"side-by-side", 0, 0, 147}, + {"side-by-side", 0, 0, 'y'}, {"unified", 2, 0, 146}, {"left-column", 0, 0, 129}, {"suppress-common-lines", 0, 0, 130}, @@ -188,28 +188,6 @@ static struct option const longopts[] = mostly to ignore -q. Maybe this should be fixed, but I think it's a larger issue than the changes included here. */ -static void strcat_and_allocate PROTO ((char **, size_t *, const char *)); - -/* *STR is a pointer to a malloc'd string. *LENP is its allocated - length. Add SRC to the end of it, reallocating if necessary. */ -static void -strcat_and_allocate (str, lenp, src) - char **str; - size_t *lenp; - const char *src; -{ - size_t new_size; - - new_size = strlen (*str) + strlen (src) + 1; - if (*str == NULL || new_size >= *lenp) - { - while (new_size >= *lenp) - *lenp *= 2; - *str = xrealloc (*str, *lenp); - } - strcat (*str, src); -} - int diff (argc, argv) int argc; @@ -248,18 +226,19 @@ diff (argc, argv) optind = 0; while ((c = getopt_long (argc, argv, - "+abcdefhilnpstuw0123456789BHNRC:D:F:I:L:U:V:W:k:r:", + "+abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:V:W:k:r:", longopts, &option_index)) != -1) { switch (c) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'h': case 'i': case 'n': case 'p': case 's': case 't': - case 'u': case 'w': case '0': case '1': case '2': - case '3': case '4': case '5': case '6': case '7': case '8': - case '9': case 'B': case 'H': + case 'u': case 'w': case 'y': + case '0': case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + case 'B': case 'H': case 'T': (void) sprintf (tmp, " -%c", (char) c); - strcat_and_allocate (&opts, &opts_allocated, tmp); + allocate_and_strcat (&opts, &opts_allocated, tmp); break; case 'L': if (have_rev1_label++) @@ -269,32 +248,31 @@ diff (argc, argv) break; } - strcat_and_allocate (&opts, &opts_allocated, " -L"); - strcat_and_allocate (&opts, &opts_allocated, optarg); + allocate_and_strcat (&opts, &opts_allocated, " -L"); + allocate_and_strcat (&opts, &opts_allocated, optarg); break; case 'C': case 'F': case 'I': case 'U': case 'V': case 'W': (void) sprintf (tmp, " -%c", (char) c); - strcat_and_allocate (&opts, &opts_allocated, tmp); - strcat_and_allocate (&opts, &opts_allocated, optarg); + allocate_and_strcat (&opts, &opts_allocated, tmp); + allocate_and_strcat (&opts, &opts_allocated, optarg); break; case 131: /* --ifdef. */ - strcat_and_allocate (&opts, &opts_allocated, " --ifdef="); - strcat_and_allocate (&opts, &opts_allocated, optarg); + allocate_and_strcat (&opts, &opts_allocated, " --ifdef="); + allocate_and_strcat (&opts, &opts_allocated, optarg); break; case 129: case 130: case 132: case 133: case 134: case 135: case 136: case 137: case 138: case 139: case 140: case 141: case 142: case 143: case 144: case 145: case 146: - case 147: case 148: - strcat_and_allocate (&opts, &opts_allocated, " --"); - strcat_and_allocate (&opts, &opts_allocated, + allocate_and_strcat (&opts, &opts_allocated, " --"); + allocate_and_strcat (&opts, &opts_allocated, longopts[option_index].name); if (longopts[option_index].has_arg == 1 || (longopts[option_index].has_arg == 2 && optarg != NULL)) { - strcat_and_allocate (&opts, &opts_allocated, "="); - strcat_and_allocate (&opts, &opts_allocated, optarg); + allocate_and_strcat (&opts, &opts_allocated, "="); + allocate_and_strcat (&opts, &opts_allocated, optarg); } break; case 'R': @@ -343,7 +321,7 @@ diff (argc, argv) options = xstrdup (""); #ifdef CLIENT_SUPPORT - if (client_active) { + if (current_parsed_root->isremote) { /* We're the client side. Fire up the remote server. */ start_server (); @@ -424,6 +402,8 @@ diff_fileproc (callerdat, finfo) char *tmp; char *tocvsPath; char *fname; + char *label1; + char *label2; /* Initialize these solely to avoid warnings from gcc -Wall about variables that might be used uninitialized. */ @@ -636,24 +616,53 @@ diff_fileproc (callerdat, finfo) copy_file (tocvsPath, finfo->file); } + /* Set up file labels appropriate for compatibility with the Larry Wall + * implementation of patch if the user didn't specify. This is irrelevant + * according to the POSIX.2 specification. + */ + label1 = NULL; + label2 = NULL; + if (!have_rev1_label) + { + if (empty_file == DIFF_ADDED) + label1 = + make_file_label (DEVNULL, NULL, NULL); + else + label1 = + make_file_label (finfo->fullname, use_rev1, vers ? vers->srcfile : NULL); + } + + if (!have_rev2_label) + { + if (empty_file == DIFF_REMOVED) + label2 = + make_file_label (DEVNULL, NULL, NULL); + else + label2 = + make_file_label (finfo->fullname, use_rev2, vers ? vers->srcfile : NULL); + } + if (empty_file == DIFF_ADDED || empty_file == DIFF_REMOVED) { - /* This is file, not fullname, because it is the "Index:" line which - is supposed to contain the directory. */ + /* This is fullname, not file, possibly despite the POSIX.2 + * specification, because that's the way all the Larry Wall + * implementations of patch (are there other implementations?) want + * things and the POSIX.2 spec appears to leave room for this. + */ cvs_output ("\ ===================================================================\n\ RCS file: ", 0); - cvs_output (finfo->file, 0); + cvs_output (finfo->fullname, 0); cvs_output ("\n", 1); cvs_output ("diff -N ", 0); - cvs_output (finfo->file, 0); + cvs_output (finfo->fullname, 0); cvs_output ("\n", 1); if (empty_file == DIFF_ADDED) { if (use_rev2 == NULL) - status = diff_exec (DEVNULL, finfo->file, opts, RUN_TTY); + status = diff_exec (DEVNULL, finfo->file, label1, label2, opts, RUN_TTY); else { int retcode; @@ -672,7 +681,7 @@ RCS file: ", 0); return err; } - status = diff_exec (DEVNULL, tmp, opts, RUN_TTY); + status = diff_exec (DEVNULL, tmp, label1, label2, opts, RUN_TTY); } } else @@ -691,22 +700,11 @@ RCS file: ", 0); return err; } - status = diff_exec (tmp, DEVNULL, opts, RUN_TTY); + status = diff_exec (tmp, DEVNULL, label1, label2, opts, RUN_TTY); } } else { - char *label1 = NULL; - char *label2 = NULL; - - if (!have_rev1_label) - label1 = - make_file_label (finfo->fullname, use_rev1, vers->srcfile); - - if (!have_rev2_label) - label2 = - make_file_label (finfo->fullname, use_rev2, vers->srcfile); - status = RCS_exec_rcsdiff (vers->srcfile, opts, *options ? options : vers->options, use_rev1, use_rev2, diff --git a/contrib/cvs/src/filesubr.c b/contrib/cvs/src/filesubr.c index 2cccbcd..0282005 100644 --- a/contrib/cvs/src/filesubr.c +++ b/contrib/cvs/src/filesubr.c @@ -21,6 +21,7 @@ * $FreeBSD$ */ +#include #include "cvs.h" static int deep_remove_dir PROTO((const char *path)); @@ -417,12 +418,12 @@ unlink_file (f) const char *f; { if (trace) - (void) fprintf (stderr, "%s-> unlink(%s)\n", + (void) fprintf (stderr, "%s-> unlink_file(%s)\n", CLIENT_SERVER_STR, f); if (noexec) return (0); - return (unlink (f)); + return (CVS_UNLINK (f)); } /* @@ -468,7 +469,7 @@ unlink_file_dir (f) else if (S_ISDIR (sb.st_mode)) return deep_remove_dir (f); - return unlink (f); + return CVS_UNLINK (f); } /* Remove a directory and everything it contains. Returns 0 for @@ -491,14 +492,14 @@ deep_remove_dir (path) returns 87). */ || (ENOTEMPTY == 17 && EEXIST == 17 && errno == 87)) { - if ((dirp = opendir (path)) == NULL) + if ((dirp = CVS_OPENDIR (path)) == NULL) /* If unable to open the directory return * an error */ return -1; errno = 0; - while ((dp = readdir (dirp)) != NULL) + while ((dp = CVS_READDIR (dirp)) != NULL) { char *buf; @@ -516,16 +517,16 @@ deep_remove_dir (path) { if (deep_remove_dir(buf)) { - closedir(dirp); + CVS_CLOSEDIR(dirp); free (buf); return -1; } } else { - if (unlink (buf) != 0) + if (CVS_UNLINK (buf) != 0) { - closedir(dirp); + CVS_CLOSEDIR(dirp); free (buf); return -1; } @@ -537,11 +538,11 @@ deep_remove_dir (path) if (errno != 0) { int save_errno = errno; - closedir (dirp); + CVS_CLOSEDIR (dirp); errno = save_errno; return -1; } - closedir (dirp); + CVS_CLOSEDIR (dirp); return rmdir (path); } else @@ -688,71 +689,167 @@ xcmp (file1, file2) } /* Generate a unique temporary filename. Returns a pointer to a newly - malloc'd string containing the name. Returns successfully or not at - all. */ -/* There are at least three functions for generating temporary - filenames. We use tempnam (SVID 3) if possible, else mktemp (BSD - 4.3), and as last resort tmpnam (POSIX). Reason is that tempnam and - mktemp both allow to specify the directory in which the temporary - file will be created. */ -#if 1 + * malloc'd string containing the name. Returns successfully or not at + * all. + * + * THIS FUNCTION IS DEPRECATED!!! USE cvs_temp_file INSTEAD!!! + * + * and yes, I know about the way the rcs commands use temp files. I think + * they should be converted too but I don't have time to look into it right + * now. + */ char * cvs_temp_name () { - char *value; - int retval; - - value = xmalloc (strlen (Tmpdir) + 40); - sprintf (value, "%s/%s", Tmpdir, "cvsXXXXXXXXXX"); - retval = mkstemp (value); + char *fn; + FILE *fp; - if (retval == -1) - error (1, errno, "cannot generate temporary filename"); - close (retval); - return value; + fp = cvs_temp_file (&fn); + if (fp == NULL) + error (1, errno, "Failed to create temporary file"); + if (fclose (fp) == EOF) + error (0, errno, "Failed to close temporary file %s", fn); + return fn; } -#else -#ifdef HAVE_TEMPNAM -char * -cvs_temp_name () + +/* Generate a unique temporary filename and return an open file stream + * to the truncated file by that name + * + * INPUTS + * filename where to place the pointer to the newly allocated file + * name string + * + * OUTPUTS + * filename dereferenced, will point to the newly allocated file + * name string. This value is undefined if the function + * returns an error. + * + * RETURNS + * An open file pointer to a read/write mode empty temporary file with the + * unique file name or NULL on failure. + * + * ERRORS + * on error, errno will be set to some value either by CVS_FOPEN or + * whatever system function is called to generate the temporary file name + */ +/* There are at least four functions for generating temporary + * filenames. We use mkstemp (BSD 4.3) if possible, else tempnam (SVID 3), + * else mktemp (BSD 4.3), and as last resort tmpnam (POSIX). Reason is that + * mkstemp, tempnam, and mktemp both allow to specify the directory in which + * the temporary file will be created. + * + * And the _correct_ way to use the deprecated functions probably involves + * opening file descriptors using O_EXCL & O_CREAT and even doing the annoying + * NFS locking thing, but until I hear of more problems, I'm not going to + * bother. + */ +FILE *cvs_temp_file (filename) + char **filename; { - char *retval; + char *fn; + FILE *fp; + + /* FIXME - I'd like to be returning NULL here in noexec mode, but I think + * some of the rcs & diff functions which rely on a temp file run in + * noexec mode too. + */ + + assert (filename != NULL); + +#ifdef HAVE_MKSTEMP + + { + int fd; + + fn = xmalloc (strlen (Tmpdir) + 11); + sprintf (fn, "%s/%s", Tmpdir, "cvsXXXXXX" ); + fd = mkstemp (fn); + + /* a NULL return will be interpreted by callers as an error and + * errno should still be set + */ + if (fd == -1) fp = NULL; + else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL) + { + /* attempt to close and unlink the file since mkstemp returned sucessfully and + * we believe it's been created and opened + */ + int save_errno = errno; + if (close (fd)) + error (0, errno, "Failed to close temporary file %s", fn); + if (CVS_UNLINK (fn)) + error (0, errno, "Failed to unlink temporary file %s", fn); + errno = save_errno; + } + + if (fp == NULL) free (fn); + /* mkstemp is defined to open mode 0600 using glibc 2.0.7+ */ + /* FIXME - configure can probably tell us which version of glibc we are + * linking to and not chmod for 2.0.7+ + */ + else chmod (fn, 0600); + + } + +#elif HAVE_TEMPNAM + + /* tempnam has been deprecated due to under-specification */ + + fn = tempnam (Tmpdir, "cvs"); + if (fn == NULL) fp = NULL; + else if ((fp = CVS_FOPEN (fn, "w+")) == NULL) free (fn); + else chmod (fn, 0600); - retval = tempnam (Tmpdir, "cvs"); - if (retval == NULL) - error (1, errno, "cannot generate temporary filename"); /* tempnam returns a pointer to a newly malloc'd string, so there's - no need for a xstrdup */ - return retval; -} -#else -char * -cvs_temp_name () -{ -# ifdef HAVE_MKTEMP - char *value; - char *retval; - - value = xmalloc (strlen (Tmpdir) + 40); - sprintf (value, "%s/%s", Tmpdir, "cvsXXXXXX" ); - retval = mktemp (value); - - if (retval == NULL) - error (1, errno, "cannot generate temporary filename"); - return value; -# else - char value[L_tmpnam + 1]; - char *retval; - - retval = tmpnam (value); - if (retval == NULL) - error (1, errno, "cannot generate temporary filename"); - return xstrdup (value); -# endif -} -#endif + * no need for a xstrdup + */ + +#elif HAVE_MKTEMP + + /* mktemp has been deprecated due to the BSD 4.3 specification specifying + * that XXXXXX will be replaced by a PID and a letter, creating only 26 + * possibilities, a security risk, and a race condition. + */ + + { + char *ifn; + + ifn = xmalloc (strlen (Tmpdir) + 11); + sprintf (ifn, "%s/%s", Tmpdir, "cvsXXXXXX" ); + fn = mktemp (ifn); + + if (fn == NULL) fp = NULL; + else fp = CVS_FOPEN (fn, "w+"); + + if (fp == NULL) free (ifn); + else chmod (fn, 0600); + + } + +#else /* use tmpnam if all else fails */ + + /* tmpnam is deprecated */ + + { + char ifn[L_tmpnam + 1]; + + fn = tmpnam (ifn); + + if (fn == NULL) fp = NULL; + else if ((fp = CVS_FOPEN (ifn, "w+")) != NULL) + { + fn = xstrdup (ifn); + chmod (fn, 0600); + } + + } + #endif - + + *filename = fn; + return fp; +} + /* Return non-zero iff FILENAME is absolute. Trivial under Unix, but more complicated under other systems. */ int @@ -841,13 +938,17 @@ char * get_homedir () { static char *home = NULL; - char *env = getenv ("HOME"); + char *env; struct passwd *pw; if (home != NULL) return home; - if (env) + if ( +#ifdef SERVER_SUPPORT + !server_active && +#endif + (env = getenv ("HOME")) != NULL) home = env; else if ((pw = (struct passwd *) getpwuid (getuid ())) && pw->pw_dir) @@ -954,7 +1055,7 @@ fopen_case (name, mode, fp, pathp) } } errno = 0; - while ((dp = readdir (dirp)) != NULL) + while ((dp = CVS_READDIR (dirp)) != NULL) { if (cvs_casecmp (dp->d_name, fname) == 0) { @@ -966,7 +1067,7 @@ fopen_case (name, mode, fp, pathp) } if (errno != 0) error (1, errno, "cannot read directory %s", dir); - closedir (dirp); + CVS_CLOSEDIR (dirp); if (found_name == NULL) { diff --git a/contrib/cvs/src/import.c b/contrib/cvs/src/import.c index 3f77dc9..9065cdd 100644 --- a/contrib/cvs/src/import.c +++ b/contrib/cvs/src/import.c @@ -173,16 +173,17 @@ import (argc, argv) if (! isabsolute (argv[0]) && pathname_levels (argv[0]) == 0) { - if (CVSroot_directory == NULL) + if (current_parsed_root == NULL) { error (0, 0, "missing CVSROOT environment variable\n"); error (1, 0, "Set it or specify the '-d' option to %s.", program_name); } - repository = xmalloc (strlen (CVSroot_directory) + strlen (argv[0]) - + 10); - (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]); - repos_len = strlen (CVSroot_directory); + repository = xmalloc (strlen (current_parsed_root->directory) + + strlen (argv[0]) + + 2); + (void) sprintf (repository, "%s/%s", current_parsed_root->directory, argv[0]); + repos_len = strlen (current_parsed_root->directory); } else { @@ -209,7 +210,7 @@ import (argc, argv) *cp = '\0'; #ifdef CLIENT_SUPPORT - if (client_active) + if (current_parsed_root->isremote) { /* For rationale behind calling start_server before do_editor, see commit.c */ @@ -238,7 +239,7 @@ import (argc, argv) } #ifdef CLIENT_SUPPORT - if (client_active) + if (current_parsed_root->isremote) { int err; @@ -292,8 +293,7 @@ import (argc, argv) make_directories (repository); /* Create the logfile that will be logged upon completion */ - tmpfile = cvs_temp_name (); - if ((logfp = CVS_FOPEN (tmpfile, "w+")) == NULL) + if ((logfp = cvs_temp_file (&tmpfile)) == NULL) error (1, errno, "cannot create temporary file `%s'", tmpfile); /* On systems where we can unlink an open file, do so, so it will go away no matter how we exit. FIXME-maybe: Should be checking for @@ -427,7 +427,7 @@ import_descend (message, vtag, targc, targv) else { errno = 0; - while ((dp = readdir (dirp)) != NULL) + while ((dp = CVS_READDIR (dirp)) != NULL) { if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0) goto one_more_time_boys; @@ -478,7 +478,7 @@ import_descend (message, vtag, targc, targv) else { #ifdef CLIENT_SUPPORT - if (client_active) + if (current_parsed_root->isremote) err += client_process_import_file (message, dp->d_name, vtag, targc, targv, repository, @@ -498,7 +498,7 @@ import_descend (message, vtag, targc, targv) error (0, errno, "cannot read directory"); ++err; } - (void) closedir (dirp); + (void) CVS_CLOSEDIR (dirp); } if (dirlist != NULL) @@ -1569,7 +1569,7 @@ import_descend_dir (message, dir, vtag, targc, targv) } #ifdef CLIENT_SUPPORT - if (!quiet && !client_active) + if (!quiet && !current_parsed_root->isremote) #else if (!quiet) #endif @@ -1584,7 +1584,7 @@ import_descend_dir (message, dir, vtag, targc, targv) goto out; } #ifdef CLIENT_SUPPORT - if (!client_active && !isdir (repository)) + if (!current_parsed_root->isremote && !isdir (repository)) #else if (!isdir (repository)) #endif diff --git a/contrib/cvs/src/lock.c b/contrib/cvs/src/lock.c index 30c72f9..7fe92c9 100644 --- a/contrib/cvs/src/lock.c +++ b/contrib/cvs/src/lock.c @@ -174,12 +174,13 @@ lock_name (repository, name) /* The interesting part of the repository is the part relative to CVSROOT. */ - assert (CVSroot_directory != NULL); - assert (strncmp (repository, CVSroot_directory, - strlen (CVSroot_directory)) == 0); - short_repos = repository + strlen (CVSroot_directory) + 1; + assert (current_parsed_root != NULL); + assert (current_parsed_root->directory != NULL); + assert (strncmp (repository, current_parsed_root->directory, + strlen (current_parsed_root->directory)) == 0); + short_repos = repository + strlen (current_parsed_root->directory) + 1; - if (strcmp (repository, CVSroot_directory) == 0) + if (strcmp (repository, current_parsed_root->directory) == 0) short_repos = "."; else assert (short_repos[-1] == '/'); @@ -640,7 +641,7 @@ again: error (1, 0, "cannot open directory %s", repository); errno = 0; - while ((dp = readdir (dirp)) != NULL) + while ((dp = CVS_READDIR (dirp)) != NULL) { if (CVS_FNMATCH (CVSRFLPAT, dp->d_name, 0) == 0) { @@ -661,7 +662,7 @@ again: */ if (now >= (sb.st_ctime + CVSLCKAGE) && CVS_UNLINK (line) != -1) { - (void) closedir (dirp); + (void) CVS_CLOSEDIR (dirp); free (line); goto again; } @@ -687,7 +688,7 @@ again: if (errno != 0) error (0, errno, "error reading directory %s", repository); - closedir (dirp); + CVS_CLOSEDIR (dirp); return (ret); } @@ -897,10 +898,11 @@ lock_filesdoneproc (callerdat, err, repository, update_dir, entries) } void -lock_tree_for_write (argc, argv, local, aflag) +lock_tree_for_write (argc, argv, local, which, aflag) int argc; char **argv; int local; + int which; int aflag; { int err; @@ -911,7 +913,7 @@ lock_tree_for_write (argc, argv, local, aflag) lock_tree_list = getlist (); err = start_recursion ((FILEPROC) NULL, lock_filesdoneproc, (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc, - argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0); + argv, local, which, aflag, 0, (char *) NULL, 0); sortlist (lock_tree_list, fsortcmp); if (Writer_Lock (lock_tree_list) != 0) error (1, 0, "lock failed - giving up"); diff --git a/contrib/cvs/src/login.c b/contrib/cvs/src/login.c index cc564c06..df6abe1 100644 --- a/contrib/cvs/src/login.c +++ b/contrib/cvs/src/login.c @@ -87,26 +87,183 @@ construct_cvspass_filename () return passfile; } -static const char *const login_usage[] = + + +/* + * static char * + * password_entry_parseline ( + * const char *cvsroot_canonical, + * const unsigned char warn, + * const int linenumber, + * char *linebuf + * ); + * + * Internal function used by password_entry_operation. Parse a single line + * from a ~/.cvsroot password file and return a pointer to the password if the + * line refers to the same cvsroot as cvsroot_canonical + * + * INPUTS + * cvsroot_canonical the root we are looking for + * warn Boolean: print warnings for invalid lines? + * linenumber the line number for error messages + * linebuf the current line + * + * RETURNS + * NULL if the line doesn't match + * char *password as a pointer into linebuf + * + * NOTES + * This function temporarily alters linebuf, so it isn't thread safe when + * called on the same linebuf + */ +static char * +password_entry_parseline (cvsroot_canonical, warn, linenumber, linebuf) + const char *cvsroot_canonical; + const unsigned char warn; + const int linenumber; + char *linebuf; { - "Usage: %s %s\n", - "(Specify the --help global option for a list of other help options)\n", - NULL -}; + char *password = NULL; + char *p; -/* Prompt for a password, and store it in the file "CVS/.cvspass". + /* look for '^/' */ + if (*linebuf == '/') + { + /* Yes: slurp '^/\d+\D' and parse the rest of the line according to version number */ + char *q; + unsigned long int entry_version; + + if (isspace(*(linebuf + 1))) + /* special case since strtoul ignores leading white space */ + entry_version = 0; + else + entry_version = strtoul (linebuf + 1, &q, 10); + + if (q == linebuf + 1) + /* no valid digits found by strtoul */ + entry_version = 0; + else + /* assume a delimiting seperator */ + q++; + + switch (entry_version) + { + case 1: + /* this means the same normalize_cvsroot we are using was + * used to create this entry. strcmp is good enough for + * us. + */ + p = strchr (q, ' '); + if (p == NULL) + { + if (warn && !really_quiet) + error (0, 0, "warning: skipping invalid entry in password file at line %d", + linenumber); + } + else + { + *p = '\0'; + if (strcmp (cvsroot_canonical, q) == 0) + password = p + 1; + *p = ' '; + } + break; + case ULONG_MAX: + if (warn && !really_quiet) + { + error (0, errno, "warning: unable to convert version number in password file at line %d", + linenumber); + error (0, 0, "skipping entry"); + } + break; + case 0: + if (warn && !really_quiet) + error (0, 0, "warning: skipping entry with invalid version string in password file at line %d", + linenumber); + break; + default: + if (warn && !really_quiet) + error (0, 0, "warning: skipping entry with unknown version (%lu) in password file at line %d", + entry_version, linenumber); + break; + } + } + else + { + /* No: assume: + * + * ^cvsroot Aencoded_password$ + * + * as header comment specifies and parse accordingly + */ + cvsroot_t *tmp_root; + char *tmp_root_canonical; + + p = strchr (linebuf, ' '); + if (p == NULL) + { + if (warn && !really_quiet) + error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber); + return NULL;; + } + + *p = '\0'; + if ((tmp_root = parse_cvsroot (linebuf)) == NULL) + { + if (warn && !really_quiet) + error (0, 0, "warning: skipping invalid entry in password file at line %d", linenumber); + *p = ' '; + return NULL; + } + *p = ' '; + tmp_root_canonical = normalize_cvsroot (tmp_root); + if (strcmp (cvsroot_canonical, tmp_root_canonical) == 0) + password = p + 1; + + free (tmp_root_canonical); + free_cvsroot_t (tmp_root); + } + + return password; +} + + + +/* + * static char * + * password_entry_operation ( + * password_entry_operation_t operation, + * cvsroot_t *root, + * char *newpassword + * ); + * + * Search the password file and depending on the value of operation: + * + * Mode Action + * password_entry_lookup Return the password + * password_entry_delete Delete the entry from the file, if it exists + * password_entry_add Replace the line with the new one, else append it * * Because the user might be accessing multiple repositories, with * different passwords for each one, the format of ~/.cvspass is: * - * user@host:/path Acleartext_password - * user@host:/path Acleartext_password + * [user@]host:[port]/path Aencoded_password + * [user@]host:[port]/path Aencoded_password * ... * - * Of course, the "user@" might be left off -- it's just based on the - * value of CVSroot. + * New entries are always of the form: + * + * /1 user@host:port/path Aencoded_password * - * The "A" before "cleartext_password" is a literal capital A. It's a + * but the old format is supported for backwards compatibility. + * The entry version string wasn't strictly necessary, but it avoids the + * overhead of parsing some entries since we know it is already in canonical + * form and allows room for expansion later, say, if we want to allow spaces + * and/or other characters to be escaped in the string. Also, the new entries + * would have been ignored by old versions of CVS anyhow since those versions + * didn't know how to parse a port number. + * + * The "A" before "encoded_password" is a literal capital A. It's a * version number indicating which form of scrambling we're doing on * the password -- someday we might provide something more secure than * the trivial encoding we do now, and when that day comes, it would @@ -116,201 +273,318 @@ static const char *const login_usage[] = * it from being read by others. Unlike .netrc, we will not be * fascist about it, at most issuing a warning, and never refusing to * work. + * + * INPUTS + * operation operation to perform + * root cvsroot_t to look up + * newpassword prescrambled new password, for password_entry_add_mode + * + * RETURNS + * -1 if password_entry_lookup_mode not specified + * NULL on failed lookup + * pointer to a copy of the password string otherwise, which the caller is + * responsible for disposing of */ -int -login (argc, argv) - int argc; - char **argv; + +typedef enum password_entry_operation_e { + password_entry_lookup, + password_entry_delete, + password_entry_add +} password_entry_operation_t; + +static char * +password_entry_operation (operation, root, newpassword) + password_entry_operation_t operation; + cvsroot_t *root; + char *newpassword; { char *passfile; FILE *fp; - char *typed_password, *found_password; - char *linebuf = (char *) NULL; - size_t linebuf_len; - int root_len, already_entered = 0; + char *cvsroot_canonical = NULL; + char *password = NULL; int line_length; + long line; + char *linebuf = NULL; + size_t linebuf_len; + char *p; + int save_errno = 0; - if (argc < 0) - usage (login_usage); + if (root->method != pserver_method) + { + error (0, 0, "internal error: can only call password_entry_operation with pserver method"); + error (1, 0, "CVSROOT: %s", root->original); + } + + /* Yes, the method below reads the user's password file twice when we have + * to delete an entry. It's inefficient, but we're not talking about a gig of + * data here. + */ - if (CVSroot_method != pserver_method) + passfile = construct_cvspass_filename (); + fp = CVS_FOPEN (passfile, "r"); + if (fp == NULL) { - error (0, 0, "can only use pserver method with `login' command"); - error (1, 0, "CVSROOT: %s", CVSroot_original); + error (0, errno, "failed to open %s for reading", passfile); + goto error_exit; } - if (! CVSroot_username) + cvsroot_canonical = normalize_cvsroot (root); + + /* Check each line to see if we have this entry already. */ + line = 0; + while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) + { + line++; + password = password_entry_parseline(cvsroot_canonical, 1, line, linebuf); + if (password != NULL) + /* this is it! break out and deal with linebuf */ + break; + } + if (line_length < 0 && !feof (fp)) { - error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", - CVSroot_original); - error (1, 0, "Please make sure to specify \"user@host\"!"); + error (0, errno, "cannot read %s", passfile); + goto error_exit; } + if (fclose (fp) < 0) + /* not fatal, unless it cascades */ + error (0, errno, "cannot close %s", passfile); + fp = NULL; - printf ("(Logging in to %s@%s)\n", CVSroot_username, CVSroot_hostname); - fflush (stdout); + /* Utter, total, raving paranoia, I know. */ + chmod (passfile, 0600); - passfile = construct_cvspass_filename (); - typed_password = GETPASS ("CVS password: "); - typed_password = scramble (typed_password); + /* a copy to return or keep around so we can reuse linebuf */ + if (password != NULL) + { + /* chomp the EOL */ + p = strchr (password, '\n'); + if (p != NULL) + *p = '\0'; + password = xstrdup (password); + } - /* Force get_cvs_password() to use this one (when the client - * confirms the new password with the server), instead of - * consulting the file. We make a new copy because cvs_password - * will get zeroed by connect_to_server(). */ + /* might as well return now */ + if (operation == password_entry_lookup) + goto out; - cvs_password = xstrdup (typed_password); + /* same here */ + if (operation == password_entry_delete && password == NULL) + { + error (0, 0, "Entry not found."); + goto out; + } - connect_to_pserver (NULL, NULL, 1, 0); + /* okay, file errors can simply be fatal from now on since we don't do + * anything else if we're in lookup mode + */ - /* IF we have a password for this "[user@]host:/path" already - * THEN - * IF it's the same as the password we read from the prompt - * THEN - * do nothing - * ELSE - * replace the old password with the new one - * ELSE - * append new entry to the end of the file. + /* copy the file with the entry deleted unless we're in add + * mode and the line we found contains the same password we're supposed to + * add */ + if (!noexec && password != NULL && (operation == password_entry_delete + || (operation == password_entry_add && strcmp (password, newpassword)))) + { + long found_at = line; + char *tmp_name; + FILE *tmp_fp; - root_len = strlen (CVSroot_original); + /* open the original file again */ + fp = CVS_FOPEN (passfile, "r"); + if (fp == NULL) + error (1, errno, "failed to open %s for reading", passfile); - /* Yes, the method below reads the user's password file twice. It's - inefficient, but we're not talking about a gig of data here. */ + /* create and open a temp file */ + if ((tmp_fp = cvs_temp_file (&tmp_name)) == NULL) + error (1, errno, "unable to open temp file %s", tmp_name); - fp = CVS_FOPEN (passfile, "r"); - /* FIXME: should be printing a message if fp == NULL and not - existence_error (errno). */ - if (fp != NULL) - { - /* Check each line to see if we have this entry already. */ + line = 0; while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) - { - if (strncmp (CVSroot_original, linebuf, root_len) == 0) - { - already_entered = 1; - break; - } - } + { + line++; + if (line < found_at + || (line != found_at + && !password_entry_parseline(cvsroot_canonical, 0, line, linebuf))) + { + if (fprintf (tmp_fp, "%s", linebuf) == EOF) + { + /* try and clean up anyhow */ + error (0, errno, "fatal error: cannot write %s", tmp_name); + if (fclose (tmp_fp) == EOF) + error (0, errno, "cannot close %s", tmp_name); + /* call CVS_UNLINK instead of unlink_file since the file + * got created in noexec mode + */ + if (CVS_UNLINK (tmp_name) < 0) + error (0, errno, "cannot remove %s", tmp_name); + /* but quit so we don't remove all the entries from a + * user's password file accidentally + */ + error (1, 0, "exiting"); + } + } + } + if (line_length < 0 && !feof (fp)) + { + error (0, errno, "cannot read %s", passfile); + goto error_exit; + } if (fclose (fp) < 0) + /* not fatal, unless it cascades */ error (0, errno, "cannot close %s", passfile); - } - else if (!existence_error (errno)) - error (0, errno, "cannot open %s", passfile); - - if (already_entered) - { - /* This user/host has a password in the file already. */ - - strtok (linebuf, " "); - found_password = strtok (NULL, "\n"); - if (strcmp (found_password, typed_password)) - { - /* typed_password and found_password don't match, so we'll - * have to update passfile. We replace the old password - * with the new one by writing a tmp file whose contents are - * exactly the same as passfile except that this one entry - * gets typed_password instead of found_password. Then we - * rename the tmp file on top of passfile. + if (fclose (tmp_fp) < 0) + /* not fatal, unless it cascades */ + /* FIXME - does copy_file return correct results if the file wasn't + * closed? should this be fatal? */ - char *tmp_name; - FILE *tmp_fp; - - tmp_name = cvs_temp_name (); - if ((tmp_fp = CVS_FOPEN (tmp_name, "w")) == NULL) - { - error (1, errno, "unable to open temp file %s", tmp_name); - return 1; - } - chmod (tmp_name, 0600); - - fp = CVS_FOPEN (passfile, "r"); - if (fp == NULL) - { - error (1, errno, "unable to open %s", passfile); - if (linebuf) - free (linebuf); - return 1; - } - /* I'm not paranoid, they really ARE out to get me: */ - chmod (passfile, 0600); - - while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) - { - if (strncmp (CVSroot_original, linebuf, root_len)) - { - if (fprintf (tmp_fp, "%s", linebuf) == EOF) - error (0, errno, "cannot write %s", tmp_name); - } - else - { - if (fprintf (tmp_fp, "%s %s\n", CVSroot_original, - typed_password) == EOF) - error (0, errno, "cannot write %s", tmp_name); - } - } - if (line_length < 0 && !feof (fp)) - error (0, errno, "cannot read %s", passfile); - if (linebuf) - free (linebuf); - if (fclose (tmp_fp) < 0) - error (0, errno, "cannot close %s", tmp_name); - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", passfile); - - /* FIXME: rename_file would make more sense (e.g. almost - always faster). */ - copy_file (tmp_name, passfile); - if (unlink_file (tmp_name) < 0) - error (0, errno, "cannot remove %s", tmp_name); - chmod (passfile, 0600); - - free (tmp_name); - } + error (0, errno, "cannot close %s", tmp_name); + + /* FIXME: rename_file would make more sense (e.g. almost + * always faster). + * + * I don't think so, unless we change the way rename_file works to + * attempt a cp/rm sequence when rename fails since rename doesn't + * work across file systems and it isn't uncommon to have /tmp + * on its own partition. + * + * For that matter, it's probably not uncommon to have a home + * directory on an NFS mount. + */ + copy_file (tmp_name, passfile); + if (CVS_UNLINK (tmp_name) < 0) + error (0, errno, "cannot remove %s", tmp_name); + free (tmp_name); } - else + + /* in add mode, if we didn't find an entry or found an entry with a + * different password, append the new line + */ + if (!noexec && operation == password_entry_add + && (password == NULL || strcmp (password, newpassword))) { - if (linebuf) - free (linebuf); if ((fp = CVS_FOPEN (passfile, "a")) == NULL) - { - error (1, errno, "could not open %s", passfile); - free (passfile); - return 1; - } - - if (fprintf (fp, "%s %s\n", CVSroot_original, typed_password) == EOF) - error (0, errno, "cannot write %s", passfile); + error (1, errno, "could not open %s for writing", passfile); + + if (fprintf (fp, "/1 %s %s\n", cvsroot_canonical, newpassword) == EOF) + error (1, errno, "cannot write %s", passfile); if (fclose (fp) < 0) error (0, errno, "cannot close %s", passfile); } /* Utter, total, raving paranoia, I know. */ chmod (passfile, 0600); + + if (password) + { + free (password); + password = NULL; + } + if (linebuf) + free (linebuf); + +out: + free (cvsroot_canonical); + free (passfile); + return password; + +error_exit: + /* just exit when we're not in lookup mode */ + if (operation != password_entry_lookup) + error (1, 0, "fatal error: exiting"); + /* clean up and exit in lookup mode so we can try a login with a NULL + * password anyhow in case that's what we would have found + */ + save_errno = errno; + if (fp != NULL) + { + /* Utter, total, raving paranoia, I know. */ + chmod (passfile, 0600); + if(fclose (fp) < 0) + error (0, errno, "cannot close %s", passfile); + } + if (linebuf) + free (linebuf); + if (cvsroot_canonical) + free (cvsroot_canonical); + free (passfile); + errno = save_errno; + return NULL; +} + + + +/* Prompt for a password, and store it in the file "CVS/.cvspass". + */ + +static const char *const login_usage[] = +{ + "Usage: %s %s\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +int +login (argc, argv) + int argc; + char **argv; +{ + char *typed_password; + char *cvsroot_canonical; + + if (argc < 0) + usage (login_usage); + + if (current_parsed_root->method != pserver_method) + { + error (0, 0, "can only use `login' command with the 'pserver' method"); + error (1, 0, "CVSROOT: %s", current_parsed_root->original); + } + + cvsroot_canonical = normalize_cvsroot(current_parsed_root); + printf ("Logging in to %s\n", cvsroot_canonical); + fflush (stdout); + + if (current_parsed_root->password) + { + typed_password = scramble (current_parsed_root->password); + } + else + { + char *tmp; + tmp = GETPASS ("CVS password: "); + typed_password = scramble (tmp); + memset (tmp, 0, strlen (tmp)); + } + + /* Force get_cvs_password() to use this one (when the client + * confirms the new password with the server), instead of + * consulting the file. We make a new copy because cvs_password + * will get zeroed by connect_to_server(). */ + cvs_password = xstrdup (typed_password); + + connect_to_pserver (NULL, NULL, 1, 0); + + password_entry_operation (password_entry_add, current_parsed_root, typed_password); + memset (typed_password, 0, strlen (typed_password)); free (typed_password); - free (passfile); free (cvs_password); + free (cvsroot_canonical); cvs_password = NULL; + return 0; } /* Returns the _scrambled_ password. The server must descramble before hashing and comparing. If password file not found, or - password not found in the file, just return NULL. */ + password not found in the file, just return NULL. */ char * get_cvs_password () { - int found_it = 0; - int root_len; - char *password = NULL; - char *linebuf = NULL; - size_t linebuf_len; - FILE *fp; - char *passfile; - int line_length; - + if (current_parsed_root->password) + return (scramble(current_parsed_root->password)); + /* If someone (i.e., login()) is calling connect_to_pserver() out of context, then assume they have supplied the correct, scrambled password. */ @@ -320,76 +594,24 @@ get_cvs_password () if (getenv ("CVS_PASSWORD") != NULL) { /* In previous versions of CVS one could specify a password in - CVS_PASSWORD. This is a bad idea, because in BSD variants - of unix anyone can see the environment variable with 'ps'. - But for users who were using that feature we want to at - least let them know what is going on. After printing this - warning, we should fall through to the regular error where - we tell them to run "cvs login" (unless they already ran - it, of course). */ - error (0, 0, "CVS_PASSWORD is no longer supported; ignored"); + * CVS_PASSWORD. This is a bad idea, because in BSD variants + * of unix anyone can see the environment variable with 'ps'. + * But for users who were using that feature we want to at + * least let them know what is going on. After printing this + * warning, we should fall through to the regular error where + * we tell them to run "cvs login" (unless they already ran + * it, of course). + */ + error (0, 0, "CVS_PASSWORD is no longer supported; ignored"); } - /* Else get it from the file. First make sure that the CVSROOT - variable has the appropriate fields filled in. */ - - if (CVSroot_method != pserver_method) + if (current_parsed_root->method != pserver_method) { - error (0, 0, "can only call GET_CVS_PASSWORD with pserver method"); - error (1, 0, "CVSROOT: %s", CVSroot_original); + error (0, 0, "can only call get_cvs_password with pserver method"); + error (1, 0, "CVSROOT: %s", current_parsed_root->original); } - if (! CVSroot_username) - { - error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", - CVSroot_original); - error (1, 0, "Please make sure to specify \"user@host\"!"); - } - - passfile = construct_cvspass_filename (); - fp = CVS_FOPEN (passfile, "r"); - if (fp == NULL) - { - free (passfile); - return NULL; - } - - root_len = strlen (CVSroot_original); - - /* Check each line to see if we have this entry already. */ - while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) - { - if (strncmp (CVSroot_original, linebuf, root_len) == 0) - { - /* This is it! So break out and deal with linebuf. */ - found_it = 1; - break; - } - } - if (line_length < 0 && !feof (fp)) - error (0, errno, "cannot read %s", passfile); - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", passfile); - - if (found_it) - { - /* linebuf now contains the line with the password. */ - char *tmp; - - strtok (linebuf, " "); - tmp = strtok (NULL, "\n"); - if (tmp == NULL) - error (1, 0, "bad entry in %s for %s", passfile, CVSroot_original); - - /* Give it permanent storage. */ - password = xstrdup (tmp); - memset (tmp, 0, strlen (password)); - } - - if (linebuf) - free (linebuf); - free (passfile); - return password; + return password_entry_operation (password_entry_lookup, current_parsed_root, NULL); } static const char *const logout_usage[] = @@ -405,29 +627,15 @@ logout (argc, argv) int argc; char **argv; { - char *passfile; - FILE *fp; - char *tmp_name = NULL; - FILE *tmp_fp; - char *linebuf = (char *) NULL; - size_t linebuf_len; - int root_len, found = 0; - int line_length; + char *cvsroot_canonical; if (argc < 0) usage (logout_usage); - if (CVSroot_method != pserver_method) + if (current_parsed_root->method != pserver_method) { error (0, 0, "can only use pserver method with `logout' command"); - error (1, 0, "CVSROOT: %s", CVSroot_original); - } - - if (! CVSroot_username) - { - error (0, 0, "CVSROOT \"%s\" is not fully-qualified.", - CVSroot_original); - error (1, 0, "Please make sure to specify \"user@host\"!"); + error (1, 0, "CVSROOT: %s", current_parsed_root->original); } /* Hmm. Do we want a variant of this command which deletes _all_ @@ -438,73 +646,15 @@ logout (argc, argv) of security, in that it wouldn't delete entries from any .cvspass files but the current one. */ - printf ("(Logging out of %s@%s)\n", CVSroot_username, CVSroot_hostname); - fflush (stdout); - - /* IF we have a password for this "[user@]host:/path" already - * THEN - * drop the entry - * ELSE - * do nothing - */ - - passfile = construct_cvspass_filename (); - /* FIXME: This should not be in /tmp; that is almost surely a security - hole. Probably should just keep it in memory. */ - tmp_name = cvs_temp_name (); - if ((tmp_fp = CVS_FOPEN (tmp_name, "w")) == NULL) + if (!quiet) { - error (1, errno, "unable to open temp file %s", tmp_name); - return 1; - } - chmod (tmp_name, 0600); - - root_len = strlen (CVSroot_original); - - fp = CVS_FOPEN (passfile, "r"); - if (fp == NULL) - error (1, errno, "Error opening %s", passfile); - - /* Check each line to see if we have this entry. */ - /* Copy only those lines that do not match this entry */ - while ((line_length = getline (&linebuf, &linebuf_len, fp)) >= 0) - { - if (strncmp (CVSroot_original, linebuf, root_len)) - { - if (fprintf (tmp_fp, "%s", linebuf) == EOF) - error (0, errno, "cannot write %s", tmp_name); - } - else - found = 1; - } - if (line_length < 0 && !feof (fp)) - error (0, errno, "cannot read %s", passfile); - - if (linebuf) - free (linebuf); - if (fclose (fp) < 0) - error (0, errno, "cannot close %s", passfile); - if (fclose (tmp_fp) < 0) - error (0, errno, "cannot close %s", tmp_name); - - if (! found) - { - printf ("Entry not found for %s\n", CVSroot_original); - if (unlink_file (tmp_name) < 0) - error (0, errno, "cannot remove %s", tmp_name); - } - else - { - /* FIXME: rename_file would make more sense (e.g. almost - always faster). */ - copy_file (tmp_name, passfile); - if (unlink_file (tmp_name) < 0) - error (0, errno, "cannot remove %s", tmp_name); - chmod (passfile, 0600); + cvsroot_canonical = normalize_cvsroot(current_parsed_root); + printf ("Logging out of %s\n", cvsroot_canonical); + fflush (stdout); + free (cvsroot_canonical); } - if (tmp_name) - free (tmp_name); + password_entry_operation (password_entry_delete, current_parsed_root, NULL); return 0; } diff --git a/contrib/cvs/src/logmsg.c b/contrib/cvs/src/logmsg.c index a5e16b7..54b3b4d 100644 --- a/contrib/cvs/src/logmsg.c +++ b/contrib/cvs/src/logmsg.c @@ -192,8 +192,10 @@ do_editor (dir, messagep, repository, changes) if (strcmp (Editor, "") == 0 && !editinfo_editor) error(1, 0, "no editor defined, must use -e or -m"); - /* Create a temporary file */ + /* FIXME - It's possible we should be relying on cvs_temp_file to open + * the file here - we get race conditions otherwise. + */ fname = cvs_temp_name (); again: if ((fp = CVS_FOPEN (fname, "w+")) == NULL) @@ -207,8 +209,6 @@ do_editor (dir, messagep, repository, changes) (*messagep)[strlen (*messagep) - 1] != '\n') (void) fprintf (fp, "\n"); } - else - (void) fprintf (fp, "\n"); if (repository != NULL) /* tack templates on if necessary */ @@ -274,7 +274,7 @@ do_editor (dir, messagep, repository, changes) free (editinfo_editor); editinfo_editor = (char *) NULL; #ifdef CLIENT_SUPPORT - if (client_active) + if (current_parsed_root->isremote) ; /* nothing, leave editinfo_editor NULL */ else #endif @@ -404,7 +404,7 @@ do_verify (messagep, repository) struct stat stbuf; #ifdef CLIENT_SUPPORT - if (client_active) + if (current_parsed_root->isremote) /* The verification will happen on the server. */ return; #endif @@ -422,13 +422,10 @@ do_verify (messagep, repository) return; } - /* Get a temp filename, open a temporary file, write the message to the + /* open a temporary file, write the message to the temp file, and close the file. */ - fname = cvs_temp_name (); - - fp = fopen (fname, "w"); - if (fp == NULL) + if ((fp = cvs_temp_file (&fname)) == NULL) error (1, errno, "cannot create temporary file %s", fname); else { @@ -849,16 +846,16 @@ logfile_write (repository, filter, message, logfp, changes) srepos = Short_Repository (repository); - prog = xmalloc ((fmt_percent - filter) + strlen (srepos) - + strlen (str_list) + strlen (fmt_continue) + prog = cp = xmalloc ((fmt_percent - filter) + 2 * strlen (srepos) + + 2 * strlen (str_list) + strlen (fmt_continue) + 10); - (void) strncpy (prog, filter, fmt_percent - filter); - prog[fmt_percent - filter] = '\0'; - (void) strcat (prog, "'"); - (void) strcat (prog, srepos); - (void) strcat (prog, str_list); - (void) strcat (prog, "'"); - (void) strcat (prog, fmt_continue); + (void) memcpy (cp, filter, fmt_percent - filter); + cp += fmt_percent - filter; + *cp++ = '"'; + cp = shell_escape (cp, srepos); + cp = shell_escape (cp, str_list); + *cp++ = '"'; + (void) strcpy (cp, fmt_continue); /* To be nice, free up some memory. */ diff --git a/contrib/cvs/src/main.c b/contrib/cvs/src/main.c index cad210b..855d2ae 100644 --- a/contrib/cvs/src/main.c +++ b/contrib/cvs/src/main.c @@ -68,8 +68,12 @@ char *Editor = EDITOR_DFLT; List *root_directories = NULL; /* We step through the above values. This variable is set to reflect - the currently active value. */ -char *current_root = NULL; + * the currently active value. + * + * Now static. FIXME - this variable should be removable (well, localizable) + * with a little more work. + */ +static char *current_root = NULL; static const struct cmd @@ -100,47 +104,50 @@ static const struct cmd char *nick2; int (*func) (); /* Function takes (argc, argv) arguments. */ + unsigned long attr; /* Attributes. */ } cmds[] = { - { "add", "ad", "new", add }, - { "admin", "adm", "rcs", admin }, - { "annotate", "ann", NULL, annotate }, - { "checkout", "co", "get", checkout }, - { "commit", "ci", "com", commit }, - { "diff", "di", "dif", diff }, - { "edit", NULL, NULL, edit }, - { "editors", NULL, NULL, editors }, - { "export", "exp", "ex", checkout }, - { "history", "hi", "his", history }, - { "import", "im", "imp", import }, - { "init", NULL, NULL, init }, + { "add", "ad", "new", add, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "admin", "adm", "rcs", admin, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "annotate", "ann", NULL, annotate, CVS_CMD_USES_WORK_DIR }, + { "checkout", "co", "get", checkout, 0 }, + { "commit", "ci", "com", commit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "diff", "di", "dif", diff, CVS_CMD_USES_WORK_DIR }, + { "edit", NULL, NULL, edit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "editors", NULL, NULL, editors, CVS_CMD_USES_WORK_DIR }, + { "export", "exp", "ex", checkout, CVS_CMD_USES_WORK_DIR }, + { "history", "hi", "his", history, CVS_CMD_USES_WORK_DIR }, + { "import", "im", "imp", import, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR | CVS_CMD_IGNORE_ADMROOT}, + { "init", NULL, NULL, init, CVS_CMD_MODIFIES_REPOSITORY }, #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT) - { "kserver", NULL, NULL, server }, /* placeholder */ + { "kserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */ #endif - { "log", "lo", "rlog", cvslog }, + { "log", "lo", NULL, cvslog, CVS_CMD_USES_WORK_DIR }, #ifdef AUTH_CLIENT_SUPPORT - { "login", "logon", "lgn", login }, - { "logout", NULL, NULL, logout }, + { "login", "logon", "lgn", login, 0 }, + { "logout", NULL, NULL, logout, 0 }, #endif /* AUTH_CLIENT_SUPPORT */ #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT) - { "pserver", NULL, NULL, server }, /* placeholder */ + { "pserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */ #endif - { "rdiff", "patch", "pa", patch }, - { "release", "re", "rel", release }, - { "remove", "rm", "delete", cvsremove }, - { "rtag", "rt", "rfreeze", rtag }, + { "rannotate","rann", "ra", annotate, 0 }, + { "rdiff", "patch", "pa", patch, 0 }, + { "release", "re", "rel", release, 0 }, + { "remove", "rm", "delete", cvsremove, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "rlog", "rl", NULL, cvslog, 0 }, + { "rtag", "rt", "rfreeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY }, #ifdef SERVER_SUPPORT - { "server", NULL, NULL, server }, + { "server", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, #endif - { "status", "st", "stat", cvsstatus }, - { "tag", "ta", "freeze", cvstag }, - { "unedit", NULL, NULL, unedit }, - { "update", "up", "upd", update }, - { "version", "ve", "ver", version }, - { "watch", NULL, NULL, watch }, - { "watchers", NULL, NULL, watchers }, - { NULL, NULL, NULL, NULL }, + { "status", "st", "stat", cvsstatus, CVS_CMD_USES_WORK_DIR }, + { "tag", "ta", "freeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "unedit", NULL, NULL, unedit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "update", "up", "upd", update, CVS_CMD_USES_WORK_DIR }, + { "version", "ve", "ver", version, 0 }, + { "watch", NULL, NULL, watch, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, + { "watchers", NULL, NULL, watchers, CVS_CMD_USES_WORK_DIR }, + { NULL, NULL, NULL, NULL, 0 }, }; static const char *const usg[] = @@ -215,9 +222,11 @@ static const char *const cmd_usage[] = #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT) " pserver Password server mode\n", #endif + " rannotate Show last revision where each line of module was modified\n", " rdiff Create 'patch' format diffs between releases\n", " release Indicate that a Module is no longer in use\n", " remove Remove an entry from the repository\n", + " rlog Print out history information for a module\n", " rtag Add a symbolic tag to a module\n", #ifdef SERVER_SUPPORT " server Server mode\n", @@ -226,6 +235,7 @@ static const char *const cmd_usage[] = " tag Add a symbolic tag to checked out version of files\n", " unedit Undo an edit command\n", " update Bring work tree in sync with repository\n", + " version Show current CVS version(s)\n", " watch Set watches\n", " watchers See who is watching a file\n", "(Specify the --help option for a list of other help options)\n", @@ -321,51 +331,14 @@ unsigned long int lookup_command_attribute (cmd_name) char *cmd_name; { - unsigned long int ret = 0; - - if (strcmp (cmd_name, "import") != 0) - { - ret |= CVS_CMD_IGNORE_ADMROOT; - } - - - /* The following commands do not use a checked-out working - directory. We conservatively assume that everything else does. - Feel free to add to this list if you are _certain_ something - something doesn't use the WD. */ - if ((strcmp (cmd_name, "checkout") != 0) && - (strcmp (cmd_name, "init") != 0) && - (strcmp (cmd_name, "login") != 0) && - (strcmp (cmd_name, "logout") != 0) && - (strcmp (cmd_name, "rdiff") != 0) && - (strcmp (cmd_name, "release") != 0) && - (strcmp (cmd_name, "rtag") != 0)) - { - ret |= CVS_CMD_USES_WORK_DIR; - } - + const struct cmd *cm; - /* The following commands do not modify the repository; we - conservatively assume that everything else does. Feel free to - add to this list if you are _certain_ something is safe. */ - if ((strcmp (cmd_name, "annotate") != 0) && - (strcmp (cmd_name, "checkout") != 0) && - (strcmp (cmd_name, "diff") != 0) && - (strcmp (cmd_name, "rdiff") != 0) && - (strcmp (cmd_name, "update") != 0) && - (strcmp (cmd_name, "editors") != 0) && - (strcmp (cmd_name, "export") != 0) && - (strcmp (cmd_name, "history") != 0) && - (strcmp (cmd_name, "log") != 0) && - (strcmp (cmd_name, "noop") != 0) && - (strcmp (cmd_name, "watchers") != 0) && - (strcmp (cmd_name, "release") != 0) && - (strcmp (cmd_name, "status") != 0)) + for (cm = cmds; cm->fullname; cm++) { - ret |= CVS_CMD_MODIFIES_REPOSITORY; + if (strcmp (cmd_name, cm->fullname) == 0) + break; } - - return ret; + return cm->attr; } @@ -593,7 +566,7 @@ main (argc, argv) version (0, (char **) NULL); (void) fputs ("\n", stdout); (void) fputs ("\ -Copyright (c) 1989-2000 Brian Berliner, david d `zoo' zuhn, \n\ +Copyright (c) 1989-2001 Brian Berliner, david d `zoo' zuhn, \n\ Jeff Polk, and other authors\n", stdout); (void) fputs ("\n", stdout); (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout); @@ -624,6 +597,8 @@ Copyright (c) 1989-2000 Brian Berliner, david d `zoo' zuhn, \n\ if (CVSroot_cmdline != NULL) free (CVSroot_cmdline); CVSroot_cmdline = xstrdup (optarg); + if (free_CVSroot) + free (CVSroot); CVSroot = xstrdup (optarg); free_CVSroot = 1; cvs_update_env = 1; /* need to update environment */ @@ -704,15 +679,6 @@ Copyright (c) 1989-2000 Brian Berliner, david d `zoo' zuhn, \n\ else command_name = cm->fullname; /* Global pointer for later use */ - /* This should probably remain a warning, rather than an error, - for quite a while. For one thing the version of VC distributed - with GNU emacs 19.34 invokes 'cvs rlog' instead of 'cvs log'. */ - if (strcmp (argv[0], "rlog") == 0) - { - error (0, 0, "warning: the rlog command is deprecated"); - error (0, 0, "use the synonymous log command instead"); - } - if (help) { argc = -1; /* some functions only check for this */ @@ -867,8 +833,7 @@ Copyright (c) 1989-2000 Brian Berliner, david d `zoo' zuhn, \n\ specify a different repository than the one we are importing to. */ - if ((lookup_command_attribute (command_name) - & CVS_CMD_IGNORE_ADMROOT) + if (!(cm->attr & CVS_CMD_IGNORE_ADMROOT) /* -d overrides CVS/Root, so don't give an error if the latter points to a nonexistent repository. */ @@ -960,25 +925,29 @@ Copyright (c) 1989-2000 Brian Berliner, david d `zoo' zuhn, \n\ variable. Parse it to see if we're supposed to do remote accesses or use a special access method. */ - if (parse_cvsroot (current_root)) + if (current_parsed_root != NULL) + free_cvsroot_t (current_parsed_root); + if ((current_parsed_root = parse_cvsroot (current_root)) == NULL) error (1, 0, "Bad CVSROOT."); if (trace) - error (0, 0, "notice: main loop with CVSROOT=%s", - current_root); + fprintf (stderr, "%s-> main loop with CVSROOT=%s\n", + CLIENT_SERVER_STR, current_root); /* * Check to see if the repository exists. */ - if (!client_active) +#ifdef CLIENT_SUPPORT + if (!current_parsed_root->isremote) +#endif /* CLIENT_SUPPORT */ { char *path; int save_errno; - path = xmalloc (strlen (CVSroot_directory) + path = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) - + 20); - (void) sprintf (path, "%s/%s", CVSroot_directory, CVSROOTADM); + + 2); + (void) sprintf (path, "%s/%s", current_parsed_root->directory, CVSROOTADM); if (!isaccessible (path, R_OK | X_OK)) { save_errno = errno; @@ -1023,7 +992,7 @@ Copyright (c) 1989-2000 Brian Berliner, david d `zoo' zuhn, \n\ && !server_active #endif #ifdef CLIENT_SUPPORT - && !client_active + && !current_parsed_root->isremote #endif ) { @@ -1031,14 +1000,18 @@ Copyright (c) 1989-2000 Brian Berliner, david d `zoo' zuhn, \n\ already printed an error. We keep going. Why? Because if we didn't, then there would be no way to check in a new CVSROOT/config file to fix the broken one! */ - parse_config (CVSroot_directory); + parse_config (current_parsed_root->directory); /* Now is a convenient time to read CVSROOT/options */ - parseopts(CVSroot_directory); + parseopts(current_parsed_root->directory); } #ifdef CLIENT_SUPPORT - if (client_active) + /* Need to check for current_parsed_root != NULL here since + * we could still be in server mode before the server function + * gets called below and sets the root + */ + if (current_parsed_root != NULL && current_parsed_root->isremote) { /* Create a new list for directory names that we've sent to the server. */ @@ -1156,31 +1129,55 @@ date_from_time_t (unixtime) void date_to_internet (dest, source) char *dest; - char *source; + const char *source; { - int year, month, day, hour, minute, second; + struct tm date; - /* Just to reiterate, these strings are from RFC822 and do not vary - according to locale. */ - static const char *const month_names[] = - {"Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + date_to_tm (&date, source); + tm_to_internet (dest, &date); +} +void +date_to_tm (dest, source) + struct tm *dest; + const char *source; +{ if (sscanf (source, SDATEFORM, - &year, &month, &day, &hour, &minute, &second) - != 6) + &dest->tm_year, &dest->tm_mon, &dest->tm_mday, + &dest->tm_hour, &dest->tm_min, &dest->tm_sec) + != 6) /* Is there a better way to handle errors here? I made this non-fatal in case we are called from the code which can't deal with fatal errors. */ error (0, 0, "internal error: bad date %s", source); - /* Always send a four digit year. */ - if (year < 100) - year += 1900; + if (dest->tm_year > 100) + dest->tm_year -= 1900; + + dest->tm_mon -= 1; +} + +/* Convert a date to RFC822/1123 format. This is used in contexts like + dates to send in the protocol; it should not vary based on locale or + other such conventions for users. We should have another routine which + does that kind of thing. - sprintf (dest, "%d %s %d %02d:%02d:%02d -0000", day, - month < 1 || month > 12 ? "???" : month_names[month - 1], - year, hour, minute, second); + The SOURCE date is a pointer to a struct tm. DEST should point to + storage managed by the caller, at least MAXDATELEN characters. */ +void +tm_to_internet (dest, source) + char *dest; + const struct tm *source; +{ + /* Just to reiterate, these strings are from RFC822 and do not vary + according to locale. */ + static const char *const month_names[] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + sprintf (dest, "%d %s %d %02d:%02d:%02d -0000", source->tm_mday, + source->tm_mon < 0 || source->tm_mon > 11 ? "???" : month_names[source->tm_mon], + source->tm_year + 1900, source->tm_hour, source->tm_min, source->tm_sec); } void diff --git a/contrib/cvs/src/mkmodules.c b/contrib/cvs/src/mkmodules.c index 49cda18..3082f7d 100644 --- a/contrib/cvs/src/mkmodules.c +++ b/contrib/cvs/src/mkmodules.c @@ -851,7 +851,7 @@ init (argc, argv) usage (init_usage); #ifdef CLIENT_SUPPORT - if (client_active) + if (current_parsed_root->isremote) { start_server (); @@ -865,12 +865,10 @@ init (argc, argv) old cvsinit.sh script did. Few utilities do that, and a non-existent parent directory is as likely to be a typo as something which needs to be created. */ - mkdir_if_needed (CVSroot_directory); + mkdir_if_needed (current_parsed_root->directory); - adm = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + 10); - strcpy (adm, CVSroot_directory); - strcat (adm, "/"); - strcat (adm, CVSROOTADM); + adm = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + 2); + sprintf (adm, "%s/%s", current_parsed_root->directory, CVSROOTADM); mkdir_if_needed (adm); /* This is needed because we pass "fileptr->filename" not "info" diff --git a/contrib/cvs/src/rcs.c b/contrib/cvs/src/rcs.c index 1e9edce..c7963b415 100644 --- a/contrib/cvs/src/rcs.c +++ b/contrib/cvs/src/rcs.c @@ -94,11 +94,6 @@ static void expand_keywords PROTO((RCSNode *, RCSVers *, const char *, size_t, char **, size_t *)); static void cmp_file_buffer PROTO((void *, const char *, size_t)); -enum rcs_delta_op {RCS_ANNOTATE, RCS_FETCH}; -static void RCS_deltas PROTO ((RCSNode *, FILE *, struct rcsbuffer *, char *, - enum rcs_delta_op, char **, size_t *, - char **, size_t *)); - /* Routines for reading, parsing and writing RCS files. */ static RCSVers *getdelta PROTO ((struct rcsbuffer *, char *, char **, char **)); @@ -2130,7 +2125,7 @@ RCS_getversion (rcs, tag, date, force_tag_match, simple_tag) * -- If tag is a branch tag, returns the branch number, not * the revision of the head of the branch. * If tag or revision is not valid or does not exist in file, - * exit with error. + * return NULL. */ char * RCS_tag2rev (rcs, tag) @@ -2209,9 +2204,8 @@ RCS_tag2rev (rcs, tag) if (rev) return rev; - error (1, 0, "tag `%s' does not exist", tag); - /* NOT REACHED -- error (1 ... ) does not return here */ - return 0; + /* Trust the caller to print warnings. */ + return NULL; } /* @@ -4184,7 +4178,7 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) whether it should be considered an error for `dest' to exist at this point. If so, the unlink call should be removed and `symlink' should signal the error. -twp) */ - if (unlink (dest) < 0 && !existence_error (errno)) + if (CVS_UNLINK (dest) < 0 && !existence_error (errno)) error (1, errno, "cannot remove %s", dest); if (symlink (info->data, dest) < 0) error (1, errno, "cannot create symbolic link from %s to %s", @@ -4354,7 +4348,7 @@ RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat) /* Unlink `dest', just in case. It's okay if this provokes a ENOENT error. */ - if (unlink (dest) < 0 && existence_error (errno)) + if (CVS_UNLINK (dest) < 0 && existence_error (errno)) error (1, errno, "cannot remove %s", dest); if (mknod (dest, special_file, devnum) < 0) error (1, errno, "could not create special file %s", @@ -5297,7 +5291,7 @@ workfile); memset (commitpt->text, 0, sizeof (Deltatext)); bufsize = 0; - switch (diff_exec (workfile, tmpfile, diffopts, changefile)) + switch (diff_exec (workfile, tmpfile, NULL, NULL, diffopts, changefile)) { case 0: case 1: @@ -5345,7 +5339,7 @@ workfile); /* This file is not being inserted at the head, but on a side branch somewhere. Make a diff from the previous revision to the working file. */ - switch (diff_exec (tmpfile, workfile, diffopts, changefile)) + switch (diff_exec (tmpfile, workfile, NULL, NULL, diffopts, changefile)) { case 0: case 1: @@ -5725,7 +5719,7 @@ RCS_setbranch (rcs, rev) int RCS_lock (rcs, rev, lock_quiet) RCSNode *rcs; - const char *rev; + char *rev; int lock_quiet; { List *locks; @@ -5744,32 +5738,16 @@ RCS_lock (rcs, rev, lock_quiet) /* A revision number of NULL means lock the head or default branch. */ if (rev == NULL) xrev = RCS_head (rcs); - - /* If rev is a branch number, lock the latest revision on that - branch. I think that if the branch doesn't exist, it's - okay to return 0 -- that just means that the branch is new, - so we don't need to lock it anyway. -twp */ - else if (RCS_nodeisbranch (rcs, rev)) - { - xrev = RCS_getbranch (rcs, (char *) rev, 1); - if (xrev == NULL) - { - if (!lock_quiet) - error (0, 0, "%s: branch %s absent", rcs->path, rev); - return 1; - } - } - - if (xrev == NULL) - xrev = xstrdup (rev); + else + xrev = RCS_gettag (rcs, rev, 1, (int *) NULL); /* Make sure that the desired revision exists. Technically, we can update the locks list without even checking this, but RCS 5.7 did this. And it can't hurt. */ - if (findnode (rcs->versions, xrev) == NULL) + if (xrev == NULL || findnode (rcs->versions, xrev) == NULL) { if (!lock_quiet) - error (0, 0, "%s: revision %s absent", rcs->path, xrev); + error (0, 0, "%s: revision %s absent", rcs->path, rev); free (xrev); return 1; } @@ -5835,7 +5813,7 @@ RCS_lock (rcs, rev, lock_quiet) int RCS_unlock (rcs, rev, unlock_quiet) RCSNode *rcs; - const char *rev; + char *rev; int unlock_quiet; { Node *lock; @@ -5885,20 +5863,15 @@ RCS_unlock (rcs, rev, unlock_quiet) return 0; /* no lock found, ergo nothing to do */ xrev = xstrdup (lock->key); } - else if (RCS_nodeisbranch (rcs, rev)) + else { - /* If rev is a branch number, unlock the latest revision on that - branch. */ - xrev = RCS_getbranch (rcs, (char *) rev, 1); + xrev = RCS_gettag (rcs, rev, 1, (int *) NULL); if (xrev == NULL) { - error (0, 0, "%s: branch %s absent", rcs->path, rev); + error (0, 0, "%s: revision %s absent", rcs->path, rev); return 1; } } - else - /* REV is an exact revision number. */ - xrev = xstrdup (rev); lock = findnode (RCS_getlocks (rcs), xrev); if (lock == NULL) @@ -6420,7 +6393,7 @@ RCS_delete_revs (rcs, tag1, tag2, inclusive) goto delrev_done; outfile = cvs_temp_name(); - status = diff_exec (beforefile, afterfile, "-an", outfile); + status = diff_exec (beforefile, afterfile, NULL, NULL, "-an", outfile); if (status == 2) { @@ -7059,7 +7032,7 @@ rcs_change_text (name, textbuf, textlen, diffbuf, difflen, retbuf, retlen) On error, give a fatal error. */ -static void +void RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen) RCSNode *rcs; FILE *fp; @@ -8418,138 +8391,6 @@ RCS_abandon (rcs) rcs->flags |= PARTIAL; } - -/* Annotate command. In rcs.c for historical reasons (from back when - what is now RCS_deltas was part of annotate_fileproc). */ - -/* Options from the command line. */ - -static int force_tag_match = 1; -static char *tag = NULL; -static char *date = NULL; - -static int annotate_fileproc PROTO ((void *callerdat, struct file_info *)); - -static int -annotate_fileproc (callerdat, finfo) - void *callerdat; - struct file_info *finfo; -{ - FILE *fp = NULL; - struct rcsbuffer *rcsbufp = NULL; - struct rcsbuffer rcsbuf; - char *version; - - if (finfo->rcs == NULL) - return (1); - - if (finfo->rcs->flags & PARTIAL) - { - RCS_reparsercsfile (finfo->rcs, &fp, &rcsbuf); - rcsbufp = &rcsbuf; - } - - version = RCS_getversion (finfo->rcs, tag, date, force_tag_match, - (int *) NULL); - if (version == NULL) - return 0; - - /* Distinguish output for various files if we are processing - several files. */ - cvs_outerr ("Annotations for ", 0); - cvs_outerr (finfo->fullname, 0); - cvs_outerr ("\n***************\n", 0); - - RCS_deltas (finfo->rcs, fp, rcsbufp, version, RCS_ANNOTATE, NULL, - NULL, NULL, NULL); - free (version); - return 0; -} - -static const char *const annotate_usage[] = -{ - "Usage: %s %s [-lRf] [-r rev|-D date] [files...]\n", - "\t-l\tLocal directory only, no recursion.\n", - "\t-R\tProcess directories recursively.\n", - "\t-f\tUse head revision if tag/date not found.\n", - "\t-r rev\tAnnotate file as of specified revision/tag.\n", - "\t-D date\tAnnotate file as of specified date.\n", - "(Specify the --help global option for a list of other help options)\n", - NULL -}; - -/* Command to show the revision, date, and author where each line of a - file was modified. */ - -int -annotate (argc, argv) - int argc; - char **argv; -{ - int local = 0; - int c; - - if (argc == -1) - usage (annotate_usage); - - optind = 0; - while ((c = getopt (argc, argv, "+lr:D:fR")) != -1) - { - switch (c) - { - case 'l': - local = 1; - break; - case 'R': - local = 0; - break; - case 'r': - tag = optarg; - break; - case 'D': - date = Make_Date (optarg); - break; - case 'f': - force_tag_match = 0; - break; - case '?': - default: - usage (annotate_usage); - break; - } - } - argc -= optind; - argv += optind; - -#ifdef CLIENT_SUPPORT - if (client_active) - { - start_server (); - ign_setup (); - - if (local) - send_arg ("-l"); - if (!force_tag_match) - send_arg ("-f"); - option_with_arg ("-r", tag); - if (date) - client_senddate (date); - send_files (argc, argv, local, 0, SEND_NO_CONTENTS); - send_file_names (argc, argv, SEND_EXPAND_WILD); - send_to_server ("annotate\012", 0); - return get_responses_and_close (); - } -#endif /* CLIENT_SUPPORT */ - - if (tag != NULL) - tag_check_valid (tag, argc, argv, local, 0, ""); - - return start_recursion (annotate_fileproc, (FILESDONEPROC) NULL, - (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, - argc, argv, local, W_LOCAL, 0, 1, (char *)NULL, - 1); -} - /* * For a given file with full pathname PATH and revision number REV, * produce a file label suitable for passing to diff. The default @@ -8559,6 +8400,11 @@ annotate (argc, argv) * * The date and time used are the revision's last checkin date and time. * If REV is NULL, use the working copy's mtime instead. + * + * /dev/null is not statted but assumed to have been created on the Epoch. + * At least using the POSIX.2 definition of patch, this should cause creation + * of files on platforms such as Windoze where the null IO device isn't named + * /dev/null to be parsed by patch properly. */ char * make_file_label (path, rev, rcs) @@ -8566,37 +8412,45 @@ make_file_label (path, rev, rcs) char *rev; RCSNode *rcs; { - char datebuf[MAXDATELEN]; + char datebuf[MAXDATELEN + 1]; char *label; - char *file; - file = last_component (path); label = (char *) xmalloc (strlen (path) - + (rev == NULL ? 0 : strlen (rev)) - + 50); + + (rev == NULL ? 0 : strlen (rev) + 1) + + MAXDATELEN + + 2); if (rev) { - char *date; + char date[MAXDATELEN + 1]; + /* revs cannot be attached to /dev/null ... duh. */ + assert (strcmp(DEVNULL, path)); RCS_getrevtime (rcs, rev, datebuf, 0); - date = printable_date (datebuf); + (void) date_to_internet (date, datebuf); (void) sprintf (label, "-L%s\t%s\t%s", path, date, rev); - free (date); } else { struct stat sb; - struct tm *wm; + struct tm *wm = NULL; - if (CVS_STAT (file, &sb) < 0) - error (0, 1, "could not get info for `%s'", path); + if (strcmp(DEVNULL, path)) + { + char *file = last_component (path); + if (CVS_STAT (file, &sb) < 0) + error (0, 1, "could not get info for `%s'", path); + else + wm = gmtime (&sb.st_mtime); + } else { - wm = gmtime (&sb.st_mtime); - (void) sprintf (datebuf, "%04d/%02d/%02d %02d:%02d:%02d", - wm->tm_year + 1900, wm->tm_mon + 1, - wm->tm_mday, wm->tm_hour, - wm->tm_min, wm->tm_sec); + time_t t = 0; + wm = gmtime(&t); + } + + if (wm) + { + (void) tm_to_internet (datebuf, wm); (void) sprintf (label, "-L%s\t%s", path, datebuf); } } @@ -8677,7 +8531,7 @@ static char * getfullCVSname(CVSname, pathstore) char *CVSname, **pathstore; { - if (CVSroot_directory) { + if (current_parsed_root->directory) { int rootlen; char *c = NULL; int alen = sizeof(ATTIC) - 1; @@ -8695,8 +8549,8 @@ getfullCVSname(CVSname, pathstore) } } - rootlen = strlen(CVSroot_directory); - if (!strncmp(*pathstore, CVSroot_directory, rootlen) && + rootlen = strlen(current_parsed_root->directory); + if (!strncmp(*pathstore, current_parsed_root->directory, rootlen) && (*pathstore)[rootlen] == '/') CVSname = (*pathstore + rootlen + 1); else diff --git a/contrib/cvs/src/rcs.h b/contrib/cvs/src/rcs.h index 397488e..e44a45c 100644 --- a/contrib/cvs/src/rcs.h +++ b/contrib/cvs/src/rcs.h @@ -181,6 +181,9 @@ typedef void (*RCSCHECKOUTPROC) PROTO ((void *, const char *, size_t)); struct rcsbuffer; #endif +/* What RCS_deltas is supposed to do. */ +enum rcs_delta_op {RCS_ANNOTATE, RCS_FETCH}; + /* * exported interfaces */ @@ -225,8 +228,8 @@ int RCS_cmp_file PROTO ((RCSNode *, char *, char *, const char *)); int RCS_settag PROTO ((RCSNode *, const char *, const char *)); int RCS_deltag PROTO ((RCSNode *, const char *)); int RCS_setbranch PROTO((RCSNode *, const char *)); -int RCS_lock PROTO ((RCSNode *, const char *, int)); -int RCS_unlock PROTO ((RCSNode *, const char *, int)); +int RCS_lock PROTO ((RCSNode *, char *, int)); +int RCS_unlock PROTO ((RCSNode *, char *, int)); int RCS_delete_revs PROTO ((RCSNode *, char *, char *, int)); void RCS_addaccess PROTO ((RCSNode *, char *)); void RCS_delaccess PROTO ((RCSNode *, char *)); @@ -236,8 +239,10 @@ void RCS_rewrite PROTO ((RCSNode *, Deltatext *, char *)); void RCS_abandon PROTO ((RCSNode *)); int rcs_change_text PROTO ((const char *, char *, size_t, const char *, size_t, char **, size_t *)); +void RCS_deltas PROTO ((RCSNode *, FILE *, struct rcsbuffer *, char *, + enum rcs_delta_op, char **, size_t *, + char **, size_t *)); void RCS_setincexc PROTO ((const char *arg)); - void RCS_setlocalid PROTO ((const char *arg)); char *make_file_label PROTO ((char *, char *, RCSNode *)); diff --git a/contrib/cvs/src/rcscmds.c b/contrib/cvs/src/rcscmds.c index bcdffd5..b233b76 100644 --- a/contrib/cvs/src/rcscmds.c +++ b/contrib/cvs/src/rcscmds.c @@ -532,9 +532,11 @@ RCS file: ", 0); message on stderr. */ int -diff_exec (file1, file2, options, out) +diff_exec (file1, file2, label1, label2, options, out) char *file1; char *file2; + char *label1; + char *label2; char *options; char *out; { @@ -577,6 +579,10 @@ diff_exec (file1, file2, options, out) /* The first word in this string is used only for error reporting. */ sprintf (args, "diff %s", options); call_diff_setup (args); + if (label1) + call_diff_arg (label1); + if (label2) + call_diff_arg (label2); call_diff_arg (file1); call_diff_arg (file2); free (args); diff --git a/contrib/cvs/src/recurse.c b/contrib/cvs/src/recurse.c index 09e1cad..0af0d56 100644 --- a/contrib/cvs/src/recurse.c +++ b/contrib/cvs/src/recurse.c @@ -158,10 +158,10 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat, #ifdef CLIENT_SUPPORT if (!just_subdirs && CVSroot_cmdline == NULL - && client_active) + && current_parsed_root->isremote) { char *root = Name_Root (NULL, update_dir); - if (root && strcmp (root, current_root) != 0) + if (root && strcmp (root, current_parsed_root->original) != 0) /* We're skipping this directory because it is for a different root. Therefore, we just want to do the subdirectories only. Processing files would @@ -205,7 +205,7 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat, program_name); } #ifdef CLIENT_SUPPORT - else if (client_active && server_started) + else if (current_parsed_root->isremote && server_started) { /* In the the case "cvs update foo bar baz", a call to send_file_names in update.c will have sent the @@ -291,7 +291,7 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat, { if ((which & W_LOCAL) && isdir (CVSADM) #ifdef CLIENT_SUPPORT - && !client_active + && !current_parsed_root->isremote #endif ) { @@ -364,8 +364,8 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat, /* FIXME (njc): in the multiroot case, we don't want to send argument commands for those top-level directories which do not contain any subdirectories which have files checked out - from current_root. If we do, and two repositories have a - module with the same name, nasty things could happen. + from current_parsed_root->original. If we do, and two repositories + have a module with the same name, nasty things could happen. This is hard. Perhaps we should send the Argument commands later in this procedure, after we've had a chance to notice @@ -441,7 +441,7 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat, "Directory xxx" command, which forces the server to descend and serve the files there. client.c (send_file_names) has also been modified to send only those arguments which are - appropriate to current_root. + appropriate to current_parsed_root->original. */ @@ -600,8 +600,9 @@ do_recursion (frame) } - process_this_directory = (strcmp (current_root, this_root) == 0); - + process_this_directory = + (strcmp (current_parsed_root->original, this_root) == 0); + free (this_root); } } @@ -711,7 +712,7 @@ do_recursion (frame) place (server_notify). For local, we can't do them here--we don't have writelocks in place, and there is no way to get writelocks here. */ - if (client_active) + if (current_parsed_root->isremote) notify_check (repository, update_dir); #endif /* CLIENT_SUPPORT */ @@ -1025,7 +1026,8 @@ but CVS uses %s for its own purposes; skipping %s directory", } - process_this_directory = (strcmp (current_root, this_root) == 0); + process_this_directory = (strcmp (current_parsed_root->original, this_root) == 0); + free (this_root); } } diff --git a/contrib/cvs/src/server.c b/contrib/cvs/src/server.c index a53dc79..525d1ce 100644 --- a/contrib/cvs/src/server.c +++ b/contrib/cvs/src/server.c @@ -20,50 +20,19 @@ #include "getline.h" #include "buffer.h" -#ifdef SERVER_SUPPORT - -#ifdef HAVE_WINSOCK_H -#include -#endif - -#if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI) -#include -#endif - -#ifdef HAVE_KERBEROS -# include -# include -# ifndef HAVE_KRB_GET_ERR_TEXT -# define krb_get_err_text(status) krb_err_txt[status] -# endif - -/* Information we need if we are going to use Kerberos encryption. */ -static C_Block kblock; -static Key_schedule sched; - -#endif - -#ifdef HAVE_GSSAPI - -#include - -#ifdef HAVE_GSSAPI_H -#include -#endif -#ifdef HAVE_GSSAPI_GSSAPI_H -#include -#endif -#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H -#include -#endif - -#ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE -#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name -#endif - +#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) +# ifdef HAVE_GSSAPI +/* This stuff isn't included solely with SERVER_SUPPORT since some of these + * functions (encryption & the like) get compiled with or without server + * support. + * + * FIXME - They should be in a different file. + */ +# include +# include "xgssapi.h" /* We use Kerberos 5 routines to map the GSSAPI credential to a user name. */ -#include +# include /* We need this to wrap data. */ static gss_ctx_id_t gcontext; @@ -73,37 +42,46 @@ static void gserver_authenticate_connection PROTO((void)); /* Whether we are already wrapping GSSAPI communication. */ static int cvs_gssapi_wrapping; -# ifdef ENCRYPTION +# ifdef ENCRYPTION /* Whether to encrypt GSSAPI communication. We use a global variable like this because we use the same buffer type (gssapi_wrap) to handle both authentication and encryption, and we don't want multiple instances of that buffer in the communication stream. */ int cvs_gssapi_encrypt; -# endif +# endif +# endif /* HAVE_GSSAPI */ +#endif /* defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT) */ +#ifdef SERVER_SUPPORT + +#ifdef HAVE_WINSOCK_H +#include #endif -/* for select */ -#include -#ifdef HAVE_SYS_BSDTYPES_H -#include +#if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI) +#include #endif -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif +#ifdef HAVE_SYSLOG_H +#include #endif -#if HAVE_SYS_SELECT_H -#include +#ifdef HAVE_KERBEROS +# include +# include +# ifndef HAVE_KRB_GET_ERR_TEXT +# define krb_get_err_text(status) krb_err_txt[status] +# endif + +/* Information we need if we are going to use Kerberos encryption. */ +static C_Block kblock; +static Key_schedule sched; + #endif +/* for select */ +#include "xselect.h" + #ifndef O_NONBLOCK #define O_NONBLOCK O_NDELAY #endif @@ -116,18 +94,16 @@ int cvs_gssapi_encrypt; #define blocking_error(err) ((err) == EAGAIN) #endif -#ifdef AUTH_SERVER_SUPPORT -#ifdef HAVE_GETSPNAM -#include -#endif -#endif /* AUTH_SERVER_SUPPORT */ - /* For initgroups(). */ #if HAVE_INITGROUPS #include #endif /* HAVE_INITGROUPS */ - -#ifdef AUTH_SERVER_SUPPORT + +# ifdef AUTH_SERVER_SUPPORT + +# ifdef HAVE_GETSPNAM +# include +# endif /* The cvs username sent by the client, which might or might not be the same as the system username the server eventually switches to @@ -143,7 +119,7 @@ static char *Pserver_Repos = NULL; CVSROOT/config. */ int system_auth = 1; -#endif /* AUTH_SERVER_SUPPORT */ +# endif /* AUTH_SERVER_SUPPORT */ /* While processing requests, this buffer accumulates data to be sent to @@ -424,10 +400,10 @@ create_adm_p (base_dir, dir) differently. */ char *empty; - empty = malloc (strlen (CVSroot_directory) + empty = malloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + sizeof (CVSNULLREPOS) - + 10); + + 3); if (! empty) { retval = ENOMEM; @@ -435,7 +411,7 @@ create_adm_p (base_dir, dir) } /* Create the directory name. */ - (void) sprintf (empty, "%s/%s/%s", CVSroot_directory, + (void) sprintf (empty, "%s/%s/%s", current_parsed_root->directory, CVSROOTADM, CVSNULLREPOS); /* Create the directory if it doesn't exist. */ @@ -601,10 +577,16 @@ print_error (status) int status; { char *msg; + char tmpstr[80]; + buf_output0 (buf_to_net, "error "); msg = strerror (status); - if (msg) - buf_output0 (buf_to_net, msg); + if (msg == NULL) + { + sprintf (tmpstr, "unknown error %d", status); + msg = tmpstr; + } + buf_output0 (buf_to_net, msg); buf_append_char (buf_to_net, '\n'); buf_flush (buf_to_net, 0); @@ -771,7 +753,7 @@ serve_root (arg) new connection. Doing this would cause interoperability headaches, so it should be a different request, if there is any reason why such a feature is needed. */ - if (CVSroot_directory != NULL) + if (current_parsed_root != NULL) { if (alloc_pending (80 + strlen (arg))) sprintf (pending_error_text, @@ -794,24 +776,27 @@ E Protocol error: Root says \"%s\" but pserver says \"%s\"", } } #endif - set_local_cvsroot (arg); + + if (current_parsed_root != NULL) + free_cvsroot_t (current_parsed_root); + current_parsed_root = local_cvsroot (arg); /* For pserver, this will already have happened, and the call will do nothing. But for rsh, we need to do it now. */ - parse_config (CVSroot_directory); + parse_config (current_parsed_root->directory); /* Now is a good time to read CVSROOT/options too. */ - parseopts(CVSroot_directory); + parseopts(current_parsed_root->directory); - path = malloc (strlen (CVSroot_directory) + path = malloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) - + 10); + + 2); if (path == NULL) { pending_error = ENOMEM; return; } - (void) sprintf (path, "%s/%s", CVSroot_directory, CVSROOTADM); + (void) sprintf (path, "%s/%s", current_parsed_root->directory, CVSROOTADM); if (!isaccessible (path, R_OK | X_OK)) { int save_errno = errno; @@ -822,13 +807,13 @@ E Protocol error: Root says \"%s\" but pserver says \"%s\"", free (path); #ifdef HAVE_PUTENV - env = malloc (strlen (CVSROOT_ENV) + strlen (CVSroot_directory) + 1 + 1); + env = malloc (strlen (CVSROOT_ENV) + strlen (current_parsed_root->directory) + 2); if (env == NULL) { pending_error = ENOMEM; return; } - (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot_directory); + (void) sprintf (env, "%s=%s", CVSROOT_ENV, current_parsed_root->directory); (void) putenv (env); /* do not free env, as putenv has control of it */ #endif @@ -871,14 +856,14 @@ server_pathname_check (path) static int outside_root PROTO ((char *)); /* Is file or directory REPOS an absolute pathname within the - CVSroot_directory? If yes, return 0. If no, set pending_error + current_parsed_root->directory? If yes, return 0. If no, set pending_error and return 1. */ static int outside_root (repos) char *repos; { size_t repos_len = strlen (repos); - size_t root_len = strlen (CVSroot_directory); + size_t root_len = strlen (current_parsed_root->directory); /* I think isabsolute (repos) should always be true, and that any RELATIVE_REPOS stuff should only be in CVS/Repository @@ -893,15 +878,15 @@ E protocol error: %s is not absolute", repos); } if (repos_len < root_len - || strncmp (CVSroot_directory, repos, root_len) != 0) + || strncmp (current_parsed_root->directory, repos, root_len) != 0) { not_within: - if (alloc_pending (strlen (CVSroot_directory) + if (alloc_pending (strlen (current_parsed_root->directory) + strlen (repos) + 80)) sprintf (pending_error_text, "\ E protocol error: directory '%s' not within root '%s'", - repos, CVSroot_directory); + repos, current_parsed_root->directory); return 1; } if (repos_len > root_len) @@ -1104,8 +1089,9 @@ dirswitch (dir, repos) (e.g., an entry like ``world -a .'') by putting /. at the end of the Repository file, so we do the same. */ if (strcmp (dir, ".") == 0 - && CVSroot_directory != NULL - && strcmp (CVSroot_directory, repos) == 0) + && current_parsed_root != NULL + && current_parsed_root->directory != NULL + && strcmp (current_parsed_root->directory, repos) == 0) { if (fprintf (f, "/.") < 0) { @@ -2435,6 +2421,9 @@ error ENOMEM Virtual memory exhausted.\n"; /* If this gives an error, not much we could do. syslog() it? */ write (STDOUT_FILENO, msg, sizeof (msg) - 1); +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_ERR, "virtual memory exhausted"); +#endif error_exit (); } @@ -2492,13 +2481,13 @@ check_command_legal_p (cmd_name) int found_it = 0; /* else */ - flen = strlen (CVSroot_directory) + flen = strlen (current_parsed_root->directory) + strlen (CVSROOTADM) + strlen (CVSROOTADM_READERS) + 3; fname = xmalloc (flen); - (void) sprintf (fname, "%s/%s/%s", CVSroot_directory, + (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, CVSROOTADM, CVSROOTADM_READERS); fp = fopen (fname, "r"); @@ -2544,13 +2533,13 @@ check_command_legal_p (cmd_name) /* Now check the writers file. */ - flen = strlen (CVSroot_directory) + flen = strlen (current_parsed_root->directory) + strlen (CVSROOTADM) + strlen (CVSROOTADM_WRITERS) + 3; fname = xmalloc (flen); - (void) sprintf (fname, "%s/%s/%s", CVSroot_directory, + (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, CVSROOTADM, CVSROOTADM_WRITERS); fp = fopen (fname, "r"); @@ -2792,7 +2781,9 @@ error \n"); close (stderr_pipe[0]); close (stderr_pipe[1]); close (protocol_pipe[0]); + close_on_exec (protocol_pipe[1]); #ifdef SERVER_FLOWCONTROL + close_on_exec (flowcontrol_pipe[0]); close (flowcontrol_pipe[1]); #endif /* SERVER_FLOWCONTROL */ @@ -2809,11 +2800,11 @@ error \n"); exitstatus = (*command) (argument_count, argument_vector); /* Output any partial lines. If the client doesn't support - "MT", we just throw out the partial line, like old versions - of CVS did, since the protocol can't support this. */ - if (supported_response ("MT") && ! buf_empty_p (saved_output)) + "MT", we go ahead and just tack on a newline since the + protocol doesn't support anything better. */ + if (! buf_empty_p (saved_output)) { - buf_output0 (protocol, "MT text "); + buf_output0 (protocol, supported_response ("MT") ? "MT text " : "M "); buf_append_buffer (protocol, saved_output); buf_output (protocol, "\n", 1); buf_send_counted (protocol); @@ -2836,7 +2827,7 @@ error \n"); struct buffer *protocol_inbuf; /* Number of file descriptors to check in select (). */ int num_to_check; - int count_needed = 0; + int count_needed = 1; #ifdef SERVER_FLOWCONTROL int have_flowcontrolled = 0; #endif /* SERVER_FLOWCONTROL */ @@ -2924,13 +2915,16 @@ error \n"); while (stdout_pipe[0] >= 0 || stderr_pipe[0] >= 0 - || protocol_pipe[0] >= 0) + || protocol_pipe[0] >= 0 + || count_needed <= 0) { fd_set readfds; fd_set writefds; int numfds; #ifdef SERVER_FLOWCONTROL int bufmemsize; + struct timeval *timeout_ptr; + struct timeval timeout; /* * See if we are swamping the remote client and filling our VM. @@ -2951,8 +2945,24 @@ error \n"); FD_ZERO (&readfds); FD_ZERO (&writefds); + + if (count_needed <= 0) + { + /* there is data pending which was read from the protocol pipe + * so don't block if we don't find any data + */ + timeout.tv_sec = 0; + timeout.tv_usec = 0; + timeout_ptr = &timeout; + } + else + { + /* block indefinately */ + timeout_ptr = NULL; + } + if (! buf_empty_p (buf_to_net)) - FD_SET (STDOUT_FILENO, &writefds); + FD_SET (STDOUT_FILENO, &writefds); if (stdout_pipe[0] >= 0) { @@ -2968,28 +2978,34 @@ error \n"); } /* This process of selecting on the three pipes means that - we might not get output in the same order in which it - was written, thus producing the well-known - "out-of-order" bug. If the child process uses - cvs_output and cvs_outerr, it will send everything on - the protocol_pipe and avoid this problem, so the - solution is to use cvs_output and cvs_outerr in the - child process. */ + we might not get output in the same order in which it + was written, thus producing the well-known + "out-of-order" bug. If the child process uses + cvs_output and cvs_outerr, it will send everything on + the protocol_pipe and avoid this problem, so the + solution is to use cvs_output and cvs_outerr in the + child process. */ do { /* This used to select on exceptions too, but as far as I know there was never any reason to do that and SCO doesn't let you select on exceptions on pipes. */ numfds = select (num_to_check, &readfds, &writefds, - (fd_set *)0, (struct timeval *)NULL); + (fd_set *)0, timeout_ptr); if (numfds < 0 - && errno != EINTR) + && errno != EINTR) { buf_output0 (buf_to_net, "E select failed\n"); print_error (errno); goto error_exit; } } while (numfds < 0); - + + if (numfds == 0) + { + FD_ZERO (&readfds); + FD_ZERO (&writefds); + } + if (FD_ISSET (STDOUT_FILENO, &writefds)) { /* What should we do with errors? syslog() them? */ @@ -3001,7 +3017,6 @@ error \n"); { int status; int count_read; - int special; status = buf_input_data (protocol_inbuf, &count_read); @@ -3024,29 +3039,49 @@ error \n"); * have. */ count_needed -= count_read; - while (count_needed <= 0) - { - count_needed = buf_copy_counted (buf_to_net, + } + /* this is still part of the protocol pipe procedure, but it is + * outside the above conditional so that unprocessed data can be + * left in the buffer and stderr/stdout can be read when a flush + * signal is received and control can return here without passing + * through the select code and maybe blocking + */ + while (count_needed <= 0) + { + int special = 0; + + count_needed = buf_copy_counted (buf_to_net, protocol_inbuf, &special); - /* What should we do with errors? syslog() them? */ - buf_send_output (buf_to_net); + /* What should we do with errors? syslog() them? */ + buf_send_output (buf_to_net); - /* If SPECIAL got set to -1, it means that the child - wants us to flush the pipe. We don't want to block - on the network, but we flush what we can. If the - client supports the 'F' command, we send it. */ - if (special == -1) + /* If SPECIAL got set to <0, it means that the child + * wants us to flush the pipe & maybe stderr or stdout. + * + * After that we break to read stderr & stdout again before + * going back to the protocol pipe + * + * Upon breaking, count_needed = 0, so the next pass will only + * perform a non-blocking select before returning here to finish + * processing data we already read from the protocol buffer + */ + if (special == -1) + { + cvs_flushout(); + break; + } + if (special == -2) + { + /* If the client supports the 'F' command, we send it. */ + if (supported_response ("F")) { - if (supported_response ("F")) - { - buf_append_char (buf_to_net, 'F'); - buf_append_char (buf_to_net, '\n'); - } - - cvs_flusherr (); + buf_append_char (buf_to_net, 'F'); + buf_append_char (buf_to_net, '\n'); } + cvs_flusherr (); + break; } } @@ -3433,6 +3468,11 @@ server_scratch (fname) * two different cases. Using the last one which happens is almost * surely correct; I haven't tracked down why they both happen (or * even verified that they are for the same file). + * + * Don't know if this is what whoever wrote the above comment was + * talking about, but this can happen in the case where a join + * removes a file - the call to Register puts the '-vers' into the + * Entries file after the file is removed */ if (entries_line != NULL) { @@ -3592,6 +3632,15 @@ serve_log (arg) } static void +serve_rlog (arg) + char *arg; +{ + /* Tell cvslog() to behave like rlog not log. */ + command_name = "rlog"; + do_cvs_command ("rlog", cvslog); +} + +static void serve_add (arg) char *arg; { @@ -3630,7 +3679,9 @@ static void serve_rtag (arg) char *arg; { - do_cvs_command ("rtag", rtag); + /* Tell cvstag() to behave like rtag not tag. */ + command_name = "rtag"; + do_cvs_command ("rtag", cvstag); } static void @@ -3754,7 +3805,10 @@ serve_init (arg) /* Fall through to do_cvs_command which will return the actual error. */ } - set_local_cvsroot (arg); + + if (current_parsed_root != NULL) + free_cvsroot_t (current_parsed_root); + current_parsed_root = local_cvsroot (arg); do_cvs_command ("init", init); } @@ -3767,6 +3821,17 @@ serve_annotate (arg) { do_cvs_command ("annotate", annotate); } + +static void serve_rannotate PROTO ((char *)); + +static void +serve_rannotate (arg) + char *arg; +{ + /* Tell annotate() to behave like rannotate not annotate. */ + command_name = "rannotate"; + do_cvs_command ("rannotate", annotate); +} static void serve_co (arg) @@ -4164,6 +4229,23 @@ CVS server internal error: unhandled case in server_updated"); output_dir (finfo->update_dir, finfo->repository); buf_output0 (protocol, finfo->file); buf_output (protocol, "\n", 1); + /* keep the vers structure up to date in case we do a join + * - if there isn't a file, it can't very well have a version number, can it? + * + * we do it here on the assumption that since we just told the client + * to remove the file/entry, it will, and we want to remember that. + * If it fails, that's the client's problem, not ours + */ + if (vers && vers->vn_user != NULL) + { + free (vers->vn_user); + vers->vn_user = NULL; + } + if (vers && vers->ts_user != NULL) + { + free (vers->ts_user); + vers->ts_user = NULL; + } } else if (scratched_file == NULL && entries_line == NULL) { @@ -4497,7 +4579,7 @@ serve_expand_modules (arg) for (i = 1; i < argument_count; i++) err += do_module (db, argument_vector[i], CHECKOUT, "Updating", expand_proc, - NULL, 0, 0, 0, + NULL, 0, 0, 0, 0, (char *) NULL); close_module (db); server_expanding = 0; @@ -4698,6 +4780,7 @@ struct request requests[] = REQ_LINE("update", serve_update, RQ_ESSENTIAL), REQ_LINE("diff", serve_diff, 0), REQ_LINE("log", serve_log, 0), + REQ_LINE("rlog", serve_rlog, 0), REQ_LINE("add", serve_add, 0), REQ_LINE("remove", serve_remove, 0), REQ_LINE("update-patches", serve_ignore, 0), @@ -4719,6 +4802,7 @@ struct request requests[] = REQ_LINE("editors", serve_editors, 0), REQ_LINE("init", serve_init, RQ_ROOTLESS), REQ_LINE("annotate", serve_annotate, 0), + REQ_LINE("rannotate", serve_rannotate, 0), REQ_LINE("noop", serve_noop, RQ_ROOTLESS), REQ_LINE("version", serve_version, RQ_ROOTLESS), REQ_LINE(NULL, NULL, 0) @@ -4752,7 +4836,7 @@ serve_valid_requests (arg) buf_flush (buf_to_net, 1); } -#ifdef sun +#ifdef SUNOS_KLUDGE /* * Delete temporary files. SIG is the signal making this happen, or * 0 if not called as a result of a signal. @@ -4766,7 +4850,7 @@ static void wait_sig (sig) if (r == command_pid) command_pid_is_dead++; } -#endif +#endif /* SUNOS_KLUDGE */ void server_cleanup (sig) @@ -4810,7 +4894,7 @@ server_cleanup (sig) /* What a bogus kludge. This disgusting code makes all kinds of assumptions about SunOS, and is only for a bug in that system. So only enable it on Suns. */ -#ifdef sun +#ifdef SUNOS_KLUDGE if (command_pid > 0) { /* To avoid crashes on SunOS due to bugs in SunOS tmpfs @@ -4883,7 +4967,7 @@ server_cleanup (sig) } } } -#endif +#endif /* SUNOS_KLUDGE */ CVS_CHDIR (Tmpdir); /* Temporarily clear noexec, so that we clean up our temp directory @@ -5021,19 +5105,25 @@ error ENOMEM Virtual memory exhausted.\n"); pending_error = status; } #ifndef CHMOD_BROKEN - else + else if (chmod (server_temp_dir, S_IRWXU) < 0) { - if (chmod (server_temp_dir, S_IRWXU) < 0) - { - int save_errno = errno; - if (alloc_pending (80 + strlen (server_temp_dir))) - sprintf (pending_error_text, + int save_errno = errno; + if (alloc_pending (80 + strlen (server_temp_dir))) + sprintf (pending_error_text, "E cannot change permissions on temporary directory %s", - server_temp_dir); - pending_error = save_errno; - } + server_temp_dir); + pending_error = save_errno; } #endif + else if (CVS_CHDIR (server_temp_dir) < 0) + { + int save_errno = errno; + if (alloc_pending (80 + strlen (server_temp_dir))) + sprintf (pending_error_text, +"E cannot change to temporary directory %s", + server_temp_dir); + pending_error = save_errno; + } } } @@ -5129,7 +5219,7 @@ error ENOMEM Virtual memory exhausted.\n"); continue; if (!(rq->flags & RQ_ROOTLESS) - && CVSroot_directory == NULL) + && current_parsed_root == NULL) { /* For commands which change the way in which data is sent and received, for example Gzip-stream, @@ -5263,10 +5353,10 @@ error 0 %s: no such user\n", username); #endif #if HAVE_PUTENV - /* Set LOGNAME and USER in the environment, in case they are - already set to something else. */ + /* Set LOGNAME, USER and CVS_USER in the environment, in case they + are already set to something else. */ { - char *env; + char *env, *cvs_user; env = xmalloc (sizeof "LOGNAME=" + strlen (username)); (void) sprintf (env, "LOGNAME=%s", username); @@ -5275,6 +5365,11 @@ error 0 %s: no such user\n", username); env = xmalloc (sizeof "USER=" + strlen (username)); (void) sprintf (env, "USER=%s", username); (void) putenv (env); + + cvs_user = NULL != CVS_Username ? CVS_Username : ""; + env = xmalloc (sizeof "CVS_USER=" + strlen (cvs_user)); + (void) sprintf (env, "CVS_USER=%s", cvs_user); + (void) putenv (env); } #endif /* HAVE_PUTENV */ } @@ -5309,7 +5404,7 @@ check_repository_password (username, password, repository, host_user_ptr) int found_it = 0; int namelen; - /* We don't use CVSroot_directory because it hasn't been set yet + /* We don't use current_parsed_root->directory because it hasn't been set yet * -- our `repository' argument came from the authentication * protocol, not the regular CVS protocol. */ @@ -5642,8 +5737,13 @@ pserver_authenticate_connection () { int on = 1; - (void) setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, - (char *) &on, sizeof on); + if (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof on) < 0) + { +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_ERR, "error setting KEEPALIVE: %m"); +#endif + } } #endif @@ -5699,13 +5799,13 @@ pserver_authenticate_connection () 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 - clients are more widespread, probably want to fix this (it is - a real pain to track down why it isn't letting you in if it - won't say why, and I am not convinced that the potential - information disclosure to an attacker outweighs this). */ + { + printf ("error 0 %s: no such repository\n", repository); +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_NOTICE, "login refused for %s", repository); +#endif goto i_hate_you; + } /* OK, now parse the config file, so we can use it to control how to check passwords. If there was an error parsing the config @@ -5721,6 +5821,13 @@ pserver_authenticate_connection () free (descrambled_password); if (host_user == NULL) { +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_NOTICE, "login failure (for %s)", repository); +#ifdef LOG_AUTHPRIV + syslog (LOG_AUTHPRIV | LOG_NOTICE, "login failure by %s / %s (for %s)", + username, descrambled_password, repository); +#endif +#endif i_hate_you: printf ("I HATE YOU\n"); fflush (stdout); @@ -5802,8 +5909,13 @@ error %s getpeername or getsockname failed\n", strerror (errno)); { int on = 1; - (void) setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, - (char *) &on, sizeof on); + if (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, + (char *) &on, sizeof on) < 0) + { +#ifdef HAVE_SYSLOG_H + syslog (LOG_DAEMON | LOG_ERR, "error setting KEEPALIVE: %m"); +#endif + } } #endif @@ -6399,13 +6511,20 @@ cvs_flusherr () #ifdef SERVER_SUPPORT if (error_use_protocol) { + /* skip the actual stderr flush in this case since the parent process + * on the server should only be writing to stdout anyhow + */ /* Flush what we can to the network, but don't block. */ buf_flush (buf_to_net, 0); } else if (server_active) { + /* make sure stderr is flushed before we send the flush count on the + * protocol pipe + */ + fflush (stderr); /* Send a special count to tell the parent to flush. */ - buf_send_special_count (protocol, -1); + buf_send_special_count (protocol, -2); } else #endif @@ -6431,7 +6550,13 @@ cvs_flushout () cvs_flushout replaces, setting stdout to line buffering in main.c, didn't get called in the server child process. But in the future it is quite plausible that we'll want to make - this case work analogously to cvs_flusherr. */ + this case work analogously to cvs_flusherr. + + FIXME - DRP - I tried to implement this and triggered the following + error: "Protocol error: uncounted data discarded". I don't need + this feature right now, so I'm not going to bother with it yet. + */ + buf_send_special_count (protocol, -1); } else #endif diff --git a/contrib/cvs/src/update.c b/contrib/cvs/src/update.c index bec2397..c92dc41 100644 --- a/contrib/cvs/src/update.c +++ b/contrib/cvs/src/update.c @@ -60,7 +60,7 @@ static int patch_file PROTO ((struct file_info *finfo, static void patch_file_write PROTO ((void *, const char *, size_t)); #endif static int merge_file PROTO ((struct file_info *finfo, Vers_TS *vers)); -static int scratch_file PROTO((struct file_info *finfo)); +static int scratch_file PROTO((struct file_info *finfo, Vers_TS *vers)); static Dtype update_dirent_proc PROTO ((void *callerdat, char *dir, char *repository, char *update_dir, List *entries)); @@ -101,6 +101,7 @@ static int force_tag_match = 1; static int update_build_dirs = 0; static int update_prune_dirs = 0; static int pipeout = 0; +static int dotemplate = 0; #ifdef SERVER_SUPPORT static int patches = 0; static int rcs_diff_patches = 0; @@ -109,7 +110,7 @@ static List *ignlist = (List *) NULL; static time_t last_register_time; static const char *const update_usage[] = { - "Usage: %s %s [-APdflRp] [-k kopt] [-r rev|-D date] [-j rev]\n", + "Usage: %s %s [-APCdflRp] [-k kopt] [-r rev] [-D date] [-j rev]\n", " [-I ign] [-W spec] [files...]\n", "\t-A\tReset any sticky tags/date/kopts.\n", "\t-P\tPrune empty directories.\n", @@ -119,7 +120,7 @@ static const char *const update_usage[] = "\t-l\tLocal directory only, no recursion.\n", "\t-R\tProcess directories recursively.\n", "\t-p\tSend updates to standard output (avoids stickiness).\n", - "\t-k kopt\tUse RCS kopt -k option on checkout.\n", + "\t-k kopt\tUse RCS kopt -k option on checkout. (is sticky)\n", "\t-r rev\tUpdate using specified revision/tag (is sticky).\n", "\t-D date\tSet date to update from (is sticky).\n", "\t-j rev\tMerge in changes made between current revision and rev.\n", @@ -235,7 +236,7 @@ update (argc, argv) argv += optind; #ifdef CLIENT_SUPPORT - if (client_active) + if (current_parsed_root->isremote) { int pass; @@ -411,7 +412,7 @@ update (argc, argv) /* call the command line interface */ err = do_update (argc, argv, options, tag, date, force_tag_match, local, update_build_dirs, aflag, update_prune_dirs, - pipeout, which, join_rev1, join_rev2, (char *) NULL); + pipeout, which, join_rev1, join_rev2, (char *) NULL, 1); /* free the space Make_Date allocated if necessary */ if (date != NULL) @@ -425,7 +426,8 @@ update (argc, argv) */ int do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag, - xprune, xpipeout, which, xjoin_rev1, xjoin_rev2, preload_update_dir) + xprune, xpipeout, which, xjoin_rev1, xjoin_rev2, preload_update_dir, + xdotemplate) int argc; char **argv; char *xoptions; @@ -441,6 +443,7 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag, char *xjoin_rev1; char *xjoin_rev2; char *preload_update_dir; + int xdotemplate; { int err = 0; char *cp; @@ -454,6 +457,7 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag, aflag = xaflag; update_prune_dirs = xprune; pipeout = xpipeout; + dotemplate = xdotemplate; /* setup the join support */ join_rev1 = xjoin_rev1; @@ -505,11 +509,15 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag, argc, argv, local, which, aflag, 1, preload_update_dir, 1); +#ifdef SERVER_SUPPORT + if (server_active) + return err; +#endif + /* see if we need to sleep before returning to avoid time-stamp races */ if (last_register_time) { - while (time ((time_t *) NULL) == last_register_time) - sleep (1); + sleep_past (last_register_time); } return (err); @@ -577,9 +585,6 @@ update_fileproc (callerdat, finfo) int retval; Ctype status; Vers_TS *vers; - int resurrecting; - - resurrecting = 0; status = Classify_File (finfo, tag, date, options, force_tag_match, aflag, &vers, pipeout); @@ -625,9 +630,7 @@ update_fileproc (callerdat, finfo) case T_MODIFIED: /* locally modified */ case T_REMOVED: /* removed but not committed */ case T_CHECKOUT: /* needs checkout */ -#ifdef SERVER_SUPPORT case T_PATCH: /* needs patch */ -#endif retval = checkout_file (finfo, vers, 0, 0, 0); break; @@ -652,8 +655,12 @@ update_fileproc (callerdat, finfo) write_letter (finfo, 'C'); break; case T_NEEDS_MERGE: /* needs merging */ - retval = merge_file (finfo, vers); - break; + if (! toss_local_changes) + { + retval = merge_file (finfo, vers); + break; + } + /* else FALL THROUGH */ case T_MODIFIED: /* locally modified */ retval = 0; if (toss_local_changes) @@ -733,8 +740,8 @@ update_fileproc (callerdat, finfo) } } break; -#ifdef SERVER_SUPPORT case T_PATCH: /* needs patch */ +#ifdef SERVER_SUPPORT if (patches) { int docheckout; @@ -756,11 +763,11 @@ update_fileproc (callerdat, finfo) break; } } +#endif /* If we're not running as a server, just check the file out. It's simpler and faster than producing and applying patches. */ /* Fall through. */ -#endif case T_CHECKOUT: /* needs checkout */ retval = checkout_file (finfo, vers, 0, 0, 1); break; @@ -773,18 +780,7 @@ update_fileproc (callerdat, finfo) retval = 0; break; case T_REMOVE_ENTRY: /* needs to be un-registered */ - retval = scratch_file (finfo); -#ifdef SERVER_SUPPORT - if (server_active && retval == 0) - { - if (vers->ts_user == NULL) - server_scratch_entry_only (); - server_updated (finfo, vers, - SERVER_UPDATED, (mode_t) -1, - (unsigned char *) NULL, - (struct buffer *) NULL); - } -#endif + retval = scratch_file (finfo, vers); break; default: /* can't ever happen :-) */ error (0, 0, @@ -799,7 +795,7 @@ update_fileproc (callerdat, finfo) join_file (finfo, vers); /* if this directory has an ignore list, add this file to it */ - if (ignlist) + if (ignlist && (status != T_UNKNOWN || vers->ts_user == NULL)) { Node *p; @@ -879,7 +875,7 @@ update_filesdone_proc (callerdat, err, repository, update_dir, entries) { /* If there is no CVS/Root file, add one */ if (!isfile (CVSADM_ROOT)) - Create_Root ((char *) NULL, CVSroot_original); + Create_Root ((char *) NULL, current_parsed_root->original); } return (err); @@ -972,7 +968,7 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries) via WriteTag. */ 0, 0, - 1); + dotemplate); rewrite_tag = 1; nonbranch = 0; Subdir_Register (entries, (char *) NULL, dir); @@ -1175,7 +1171,7 @@ isemptydir (dir, might_not_exist) return (0); } errno = 0; - while ((dp = readdir (dirp)) != NULL) + while ((dp = CVS_READDIR (dirp)) != NULL) { if (strcmp (dp->d_name, ".") != 0 && strcmp (dp->d_name, "..") != 0) @@ -1184,7 +1180,7 @@ isemptydir (dir, might_not_exist) { /* An entry other than the CVS directory. The directory is certainly not empty. */ - (void) closedir (dirp); + (void) CVS_CLOSEDIR (dirp); return (0); } else @@ -1215,7 +1211,7 @@ isemptydir (dir, might_not_exist) { /* There are files that have been removed, but not committed! Do not consider the directory empty. */ - (void) closedir (dirp); + (void) CVS_CLOSEDIR (dirp); return (0); } } @@ -1225,10 +1221,10 @@ isemptydir (dir, might_not_exist) if (errno != 0) { error (0, errno, "cannot read directory %s", dir); - (void) closedir (dirp); + (void) CVS_CLOSEDIR (dirp); return (0); } - (void) closedir (dirp); + (void) CVS_CLOSEDIR (dirp); return (1); } @@ -1236,13 +1232,46 @@ isemptydir (dir, might_not_exist) * scratch the Entries file entry associated with a file */ static int -scratch_file (finfo) +scratch_file (finfo, vers) struct file_info *finfo; + Vers_TS *vers; { history_write ('W', finfo->update_dir, "", finfo->file, finfo->repository); Scratch_Entry (finfo->entries, finfo->file); +#ifdef SERVER_SUPPORT + if (server_active) + { + if (vers->ts_user == NULL) + server_scratch_entry_only (); + server_updated (finfo, vers, + SERVER_UPDATED, (mode_t) -1, + (unsigned char *) NULL, + (struct buffer *) NULL); + } +#endif if (unlink_file (finfo->file) < 0 && ! existence_error (errno)) error (0, errno, "unable to remove %s", finfo->fullname); + else +#ifdef SERVER_SUPPORT + /* skip this step when the server is running since + * server_updated should have handled it */ + if (!server_active) +#endif + { + /* keep the vers structure up to date in case we do a join + * - if there isn't a file, it can't very well have a version number, can it? + */ + if (vers->vn_user != NULL) + { + free (vers->vn_user); + vers->vn_user = NULL; + } + if (vers->ts_user != NULL) + { + free (vers->ts_user); + vers->ts_user = NULL; + } + } return (0); } @@ -1742,7 +1771,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) diff_options = "-n"; } - retcode = diff_exec (file1, file2, diff_options, finfo->file); + retcode = diff_exec (file1, file2, NULL, NULL, diff_options, finfo->file); /* A retcode of 0 means no differences. 1 means some differences. */ if (retcode != 0 @@ -2108,6 +2137,17 @@ join_file (finfo, vers) char *jdate1; char *jdate2; + if (trace) + fprintf (stderr, "%s-> join_file(%s, %s%s%s%s, %s, %s)\n", + CLIENT_SERVER_STR, + finfo->file, + vers->tag ? vers->tag : "", + vers->tag ? " (" : "", + vers->vn_rcs ? vers->vn_rcs : "", + vers->tag ? ")" : "", + join_rev1 ? join_rev1 : "", + join_rev2 ? join_rev2 : ""); + jrev1 = join_rev1; jrev2 = join_rev2; jdate1 = date_rev1; @@ -2276,7 +2316,14 @@ join_file (finfo, vers) for removal. FIXME: If we are doing a checkout, this has the effect of first checking out the file, and then removing it. It would be better to just register the - removal. */ + removal. + + The same goes for a removal then an add. e.g. + cvs up -rbr -jbr2 could remove and readd the same file + */ + /* save the rev since server_updated might invalidate it */ + mrev = xmalloc (strlen (vers->vn_user) + 2); + sprintf (mrev, "-%s", vers->vn_user); #ifdef SERVER_SUPPORT if (server_active) { @@ -2285,8 +2332,6 @@ join_file (finfo, vers) (unsigned char *) NULL, (struct buffer *) NULL); } #endif - mrev = xmalloc (strlen (vers->vn_user) + 2); - sprintf (mrev, "-%s", vers->vn_user); Register (finfo->entries, finfo->file, mrev, vers->ts_rcs, vers->options, vers->tag, vers->date, vers->ts_conflict); free (mrev); @@ -2329,6 +2374,7 @@ join_file (finfo, vers) addition. */ if (vers->vn_user == NULL) { + char *saved_options = options; Vers_TS *xvers; xvers = Version_TS (finfo, vers->options, jrev2, jdate2, 1, 0); @@ -2343,6 +2389,7 @@ join_file (finfo, vers) /* FIXME: If checkout_file fails, we should arrange to return a non-zero exit status. */ status = checkout_file (finfo, xvers, 1, 0, 1); + options = saved_options; freevers_ts (&xvers); -- cgit v1.1