126801Speter/* CVS client-related stuff. 217721Speter 326801Speter This program is free software; you can redistribute it and/or modify 426801Speter it under the terms of the GNU General Public License as published by 526801Speter the Free Software Foundation; either version 2, or (at your option) 626801Speter any later version. 726801Speter 826801Speter This program is distributed in the hope that it will be useful, 926801Speter but WITHOUT ANY WARRANTY; without even the implied warranty of 1026801Speter MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1126801Speter GNU General Public License for more details. */ 1226801Speter 1355519Speter/* 1455519Speter * $FreeBSD$ 1555519Speter */ 1655519Speter 1717721Speter#ifdef HAVE_CONFIG_H 18102843Speter# include "config.h" 1917721Speter#endif /* HAVE_CONFIG_H */ 2017721Speter 2125839Speter#include <assert.h> 2217721Speter#include "cvs.h" 2317721Speter#include "getline.h" 2417721Speter#include "edit.h" 2525839Speter#include "buffer.h" 26128269Speter#include "savecwd.h" 2717721Speter 2817721Speter#ifdef CLIENT_SUPPORT 2917721Speter 30102843Speter# include "md5.h" 3117721Speter 32128269Speter# if defined(AUTH_CLIENT_SUPPORT) || defined(HAVE_KERBEROS) || defined(HAVE_GSSAPI) || defined(SOCK_ERRNO) || defined(SOCK_STRERROR) 33102843Speter# ifdef HAVE_WINSOCK_H 34102843Speter# include <winsock.h> 35102843Speter# else /* No winsock.h */ 36102843Speter# include <sys/socket.h> 37102843Speter# include <netinet/in.h> 38102843Speter# include <arpa/inet.h> 39102843Speter# include <netdb.h> 40102843Speter# endif /* No winsock.h */ 41102843Speter# endif 4217721Speter 4325839Speter/* If SOCK_ERRNO is defined, then send()/recv() and other socket calls 4425839Speter do not set errno, but that this macro should be used to obtain an 4525839Speter error code. This probably doesn't make sense unless 4625839Speter NO_SOCKET_TO_FD is also defined. */ 47102843Speter# ifndef SOCK_ERRNO 48102843Speter# define SOCK_ERRNO errno 49102843Speter# endif 5017721Speter 5125839Speter/* If SOCK_STRERROR is defined, then the error codes returned by 5225839Speter socket operations are not known to strerror, and this macro must be 5325839Speter used instead to convert those error codes to strings. */ 54102843Speter# ifndef SOCK_STRERROR 55102843Speter# define SOCK_STRERROR strerror 5625839Speter 57102843Speter# if STDC_HEADERS 58102843Speter# include <string.h> 59102843Speter# endif 6025839Speter 61102843Speter# ifndef strerror 6225839Speterextern char *strerror (); 63102843Speter# endif 64102843Speter# endif /* ! SOCK_STRERROR */ 6525839Speter 66102843Speter# if HAVE_KERBEROS 6717721Speter 68102843Speter# include <krb.h> 6917721Speter 7017721Speterextern char *krb_realmofhost (); 71102843Speter# ifndef HAVE_KRB_GET_ERR_TEXT 72102843Speter# define krb_get_err_text(status) krb_err_txt[status] 73102843Speter# endif /* HAVE_KRB_GET_ERR_TEXT */ 7425839Speter 7525839Speter/* Information we need if we are going to use Kerberos encryption. */ 7625839Speterstatic C_Block kblock; 7725839Speterstatic Key_schedule sched; 7825839Speter 79102843Speter# endif /* HAVE_KERBEROS */ 8017721Speter 81102843Speter# ifdef HAVE_GSSAPI 8232785Speter 83102843Speter# include "xgssapi.h" 8432785Speter 8532785Speter/* This is needed for GSSAPI encryption. */ 8632785Speterstatic gss_ctx_id_t gcontext; 8732785Speter 88102843Speterstatic int connect_to_gserver PROTO((cvsroot_t *, int, struct hostent *)); 8932785Speter 90102843Speter# endif /* HAVE_GSSAPI */ 9181407Speter 9217721Speter 93128269Speter 94128269Speter/* Keep track of any paths we are sending for Max-dotdot so that we can verify 95128269Speter * that uplevel paths coming back form the server are valid. 96128269Speter * 97128269Speter * FIXME: The correct way to do this is probably provide some sort of virtual 98128269Speter * path map on the client side. This would be generic enough to be applied to 99128269Speter * absolute paths supplied by the user too. 100128269Speter */ 101128269Speterstatic List *uppaths = NULL; 102128269Speter 103128269Speter 104128269Speter 105128269Speterstatic void add_prune_candidate PROTO((const char *)); 106128269Speter 10717721Speter/* All the commands. */ 10817721Speterint add PROTO((int argc, char **argv)); 10917721Speterint admin PROTO((int argc, char **argv)); 11017721Speterint checkout PROTO((int argc, char **argv)); 11117721Speterint commit PROTO((int argc, char **argv)); 11217721Speterint diff PROTO((int argc, char **argv)); 11317721Speterint history PROTO((int argc, char **argv)); 11417721Speterint import PROTO((int argc, char **argv)); 11517721Speterint cvslog PROTO((int argc, char **argv)); 11617721Speterint patch PROTO((int argc, char **argv)); 11717721Speterint release PROTO((int argc, char **argv)); 11817721Speterint cvsremove PROTO((int argc, char **argv)); 11917721Speterint rtag PROTO((int argc, char **argv)); 12017721Speterint status PROTO((int argc, char **argv)); 12117721Speterint tag PROTO((int argc, char **argv)); 12217721Speterint update PROTO((int argc, char **argv)); 12317721Speter 12417721Speter/* All the response handling functions. */ 12517721Speterstatic void handle_ok PROTO((char *, int)); 12617721Speterstatic void handle_error PROTO((char *, int)); 12717721Speterstatic void handle_valid_requests PROTO((char *, int)); 12817721Speterstatic void handle_checked_in PROTO((char *, int)); 12917721Speterstatic void handle_new_entry PROTO((char *, int)); 13017721Speterstatic void handle_checksum PROTO((char *, int)); 13117721Speterstatic void handle_copy_file PROTO((char *, int)); 13217721Speterstatic void handle_updated PROTO((char *, int)); 13317721Speterstatic void handle_merged PROTO((char *, int)); 13417721Speterstatic void handle_patched PROTO((char *, int)); 13525839Speterstatic void handle_rcs_diff PROTO((char *, int)); 13617721Speterstatic void handle_removed PROTO((char *, int)); 13717721Speterstatic void handle_remove_entry PROTO((char *, int)); 13817721Speterstatic void handle_set_static_directory PROTO((char *, int)); 13917721Speterstatic void handle_clear_static_directory PROTO((char *, int)); 14017721Speterstatic void handle_set_sticky PROTO((char *, int)); 14117721Speterstatic void handle_clear_sticky PROTO((char *, int)); 14217721Speterstatic void handle_module_expansion PROTO((char *, int)); 14332896Speterstatic void handle_wrapper_rcs_option PROTO((char *, int)); 14417721Speterstatic void handle_m PROTO((char *, int)); 14517721Speterstatic void handle_e PROTO((char *, int)); 14625839Speterstatic void handle_f PROTO((char *, int)); 14717721Speterstatic void handle_notified PROTO((char *, int)); 14817721Speter 14917721Speterstatic size_t try_read_from_server PROTO ((char *, size_t)); 15054427Speter 151102843Speterstatic void auth_server PROTO ((cvsroot_t *, struct buffer *, struct buffer *, 152102843Speter int, int, struct hostent *)); 153102843Speter 15454427Speter/* We need to keep track of the list of directories we've sent to the 15554427Speter server. This list, along with the current CVSROOT, will help us 15654427Speter decide which command-line arguments to send. */ 15754427SpeterList *dirs_sent_to_server = NULL; 15854427Speter 15954427Speterstatic int is_arg_a_parent_or_listed_dir PROTO((Node *, void *)); 16054427Speter 16154427Speterstatic int 16254427Speteris_arg_a_parent_or_listed_dir (n, d) 16354427Speter Node *n; 16454427Speter void *d; 16554427Speter{ 16654427Speter char *directory = n->key; /* name of the dir sent to server */ 167177394Sobrien char *this_argv_elem = xstrdup (d); /* this argv element */ 168177394Sobrien int retval; 16954427Speter 17054427Speter /* Say we should send this argument if the argument matches the 17154427Speter beginning of a directory name sent to the server. This way, 17254427Speter the server will know to start at the top of that directory 17354427Speter hierarchy and descend. */ 17454427Speter 175177394Sobrien strip_trailing_slashes (this_argv_elem); 17654427Speter if (strncmp (directory, this_argv_elem, strlen (this_argv_elem)) == 0) 177177394Sobrien retval = 1; 178177394Sobrien else 179177394Sobrien retval = 0; 18054427Speter 181177394Sobrien free (this_argv_elem); 182177394Sobrien return retval; 18354427Speter} 18454427Speter 18554427Speterstatic int arg_should_not_be_sent_to_server PROTO((char *)); 18654427Speter 18754427Speter/* Return nonzero if this argument should not be sent to the 18854427Speter server. */ 18954427Speter 19054427Speterstatic int 19154427Speterarg_should_not_be_sent_to_server (arg) 19254427Speter char *arg; 19354427Speter{ 19454427Speter /* Decide if we should send this directory name to the server. We 19554427Speter should always send argv[i] if: 19654427Speter 19754427Speter 1) the list of directories sent to the server is empty (as it 19854427Speter will be for checkout, etc.). 19954427Speter 20054427Speter 2) the argument is "." 20154427Speter 20254427Speter 3) the argument is a file in the cwd and the cwd is checked out 20354427Speter from the current root 20454427Speter 20554427Speter 4) the argument lies within one of the paths in 20654427Speter dirs_sent_to_server. 20754427Speter 20881407Speter */ 20954427Speter 21054427Speter if (list_isempty (dirs_sent_to_server)) 21154427Speter return 0; /* always send it */ 21254427Speter 21354427Speter if (strcmp (arg, ".") == 0) 21454427Speter return 0; /* always send it */ 21554427Speter 21654427Speter /* We should send arg if it is one of the directories sent to the 21754427Speter server or the parent of one; this tells the server to descend 21854427Speter the hierarchy starting at this level. */ 21954427Speter if (isdir (arg)) 22054427Speter { 22154427Speter if (walklist (dirs_sent_to_server, is_arg_a_parent_or_listed_dir, arg)) 22254427Speter return 0; 22354427Speter 22454427Speter /* If arg wasn't a parent, we don't know anything about it (we 22554427Speter would have seen something related to it during the 22654427Speter send_files phase). Don't send it. */ 22754427Speter return 1; 22854427Speter } 22954427Speter 23054427Speter /* Try to decide whether we should send arg to the server by 23154427Speter checking the contents of the corresponding CVSADM directory. */ 23254427Speter { 233175266Sobrien char *t, *root_string; 234175266Sobrien cvsroot_t *this_root = NULL; 23554427Speter 23654427Speter /* Calculate "dirname arg" */ 23754427Speter for (t = arg + strlen (arg) - 1; t >= arg; t--) 23854427Speter { 23954427Speter if (ISDIRSEP(*t)) 24054427Speter break; 24154427Speter } 24254427Speter 24354427Speter /* Now we're either poiting to the beginning of the 24454427Speter string, or we found a path separator. */ 24554427Speter if (t >= arg) 24654427Speter { 24754427Speter /* Found a path separator. */ 24854427Speter char c = *t; 24954427Speter *t = '\0'; 25054427Speter 25154427Speter /* First, check to see if we sent this directory to the 25254427Speter server, because it takes less time than actually 25354427Speter opening the stuff in the CVSADM directory. */ 25454427Speter if (walklist (dirs_sent_to_server, is_arg_a_parent_or_listed_dir, 25554427Speter arg)) 25654427Speter { 25754427Speter *t = c; /* make sure to un-truncate the arg */ 25854427Speter return 0; 25954427Speter } 26054427Speter 26154427Speter /* Since we didn't find it in the list, check the CVSADM 26254427Speter files on disk. */ 26354427Speter this_root = Name_Root (arg, (char *) NULL); 264175266Sobrien root_string = this_root->original; 26554427Speter *t = c; 26654427Speter } 26754427Speter else 26854427Speter { 26954427Speter /* We're at the beginning of the string. Look at the 27054427Speter CVSADM files in cwd. */ 271175266Sobrien if (CVSroot_cmdline) 272175266Sobrien root_string = CVSroot_cmdline; 273175266Sobrien else 274175266Sobrien { 275175266Sobrien this_root = Name_Root ((char *) NULL, (char *) NULL); 276175266Sobrien root_string = this_root->original; 277175266Sobrien } 27854427Speter } 27954427Speter 28054427Speter /* Now check the value for root. */ 281175266Sobrien if (CVSroot_cmdline == NULL && 282175266Sobrien root_string && current_parsed_root 283175266Sobrien && (strcmp (root_string, current_parsed_root->original) != 0)) 28454427Speter { 28554427Speter /* Don't send this, since the CVSROOTs don't match. */ 286175266Sobrien if (this_root) free_cvsroot_t (this_root); 28754427Speter return 1; 28854427Speter } 289175266Sobrien if (this_root) free_cvsroot_t (this_root); 29054427Speter } 29154427Speter 29254427Speter /* OK, let's send it. */ 29354427Speter return 0; 29454427Speter} 29554427Speter 29654427Speter 297102843Speter 29854427Speter#endif /* CLIENT_SUPPORT */ 299102843Speter 300102843Speter 301102843Speter 30217721Speter#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT) 30317721Speter 30417721Speter/* Shared with server. */ 30517721Speter 30617721Speter/* 30717721Speter * Return a malloc'd, '\0'-terminated string 30817721Speter * corresponding to the mode in SB. 30917721Speter */ 31017721Speterchar * 31117721Speter#ifdef __STDC__ 31217721Spetermode_to_string (mode_t mode) 31317721Speter#else /* ! __STDC__ */ 31417721Spetermode_to_string (mode) 31517721Speter mode_t mode; 31617721Speter#endif /* __STDC__ */ 31717721Speter{ 31825839Speter char buf[18], u[4], g[4], o[4]; 31925839Speter int i; 32017721Speter 32125839Speter i = 0; 32225839Speter if (mode & S_IRUSR) u[i++] = 'r'; 32325839Speter if (mode & S_IWUSR) u[i++] = 'w'; 32425839Speter if (mode & S_IXUSR) u[i++] = 'x'; 32525839Speter u[i] = '\0'; 32617721Speter 32725839Speter i = 0; 32825839Speter if (mode & S_IRGRP) g[i++] = 'r'; 32925839Speter if (mode & S_IWGRP) g[i++] = 'w'; 33025839Speter if (mode & S_IXGRP) g[i++] = 'x'; 33125839Speter g[i] = '\0'; 33225839Speter 33325839Speter i = 0; 33425839Speter if (mode & S_IROTH) o[i++] = 'r'; 33525839Speter if (mode & S_IWOTH) o[i++] = 'w'; 33625839Speter if (mode & S_IXOTH) o[i++] = 'x'; 33725839Speter o[i] = '\0'; 33825839Speter 33925839Speter sprintf(buf, "u=%s,g=%s,o=%s", u, g, o); 34025839Speter return xstrdup(buf); 34117721Speter} 34217721Speter 34317721Speter/* 34417721Speter * Change mode of FILENAME to MODE_STRING. 34517721Speter * Returns 0 for success or errno code. 34634461Speter * If RESPECT_UMASK is set, then honor the umask. 34717721Speter */ 34817721Speterint 34934461Speterchange_mode (filename, mode_string, respect_umask) 35017721Speter char *filename; 35117721Speter char *mode_string; 35234461Speter int respect_umask; 35317721Speter{ 35417721Speter#ifdef CHMOD_BROKEN 35517721Speter char *p; 35617721Speter int writeable = 0; 35717721Speter 35817721Speter /* We can only distinguish between 35917721Speter 1) readable 36017721Speter 2) writeable 36117721Speter 3) Picasso's "Blue Period" 36217721Speter We handle the first two. */ 36317721Speter p = mode_string; 36417721Speter while (*p != '\0') 36517721Speter { 36617721Speter if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=') 36717721Speter { 36817721Speter char *q = p + 2; 36917721Speter while (*q != ',' && *q != '\0') 37017721Speter { 37117721Speter if (*q == 'w') 37217721Speter writeable = 1; 37317721Speter ++q; 37417721Speter } 37517721Speter } 37617721Speter /* Skip to the next field. */ 37717721Speter while (*p != ',' && *p != '\0') 37817721Speter ++p; 37917721Speter if (*p == ',') 38017721Speter ++p; 38117721Speter } 38217721Speter 38334461Speter /* xchmod honors the umask for us. In the !respect_umask case, we 38434461Speter don't try to cope with it (probably to handle that well, the server 38534461Speter needs to deal with modes in data structures, rather than via the 38634461Speter modes in temporary files). */ 38717721Speter xchmod (filename, writeable); 38817721Speter return 0; 38917721Speter 39017721Speter#else /* ! CHMOD_BROKEN */ 39117721Speter 39217721Speter char *p; 39317721Speter mode_t mode = 0; 39434461Speter mode_t oumask; 39517721Speter 39617721Speter p = mode_string; 39717721Speter while (*p != '\0') 39817721Speter { 39917721Speter if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=') 40017721Speter { 40117721Speter int can_read = 0, can_write = 0, can_execute = 0; 40217721Speter char *q = p + 2; 40317721Speter while (*q != ',' && *q != '\0') 40417721Speter { 40517721Speter if (*q == 'r') 40617721Speter can_read = 1; 40717721Speter else if (*q == 'w') 40817721Speter can_write = 1; 40917721Speter else if (*q == 'x') 41017721Speter can_execute = 1; 41117721Speter ++q; 41217721Speter } 41317721Speter if (p[0] == 'u') 41417721Speter { 41517721Speter if (can_read) 41617721Speter mode |= S_IRUSR; 41717721Speter if (can_write) 41817721Speter mode |= S_IWUSR; 41917721Speter if (can_execute) 42017721Speter mode |= S_IXUSR; 42117721Speter } 42217721Speter else if (p[0] == 'g') 42317721Speter { 42417721Speter if (can_read) 42517721Speter mode |= S_IRGRP; 42617721Speter if (can_write) 42717721Speter mode |= S_IWGRP; 42817721Speter if (can_execute) 42917721Speter mode |= S_IXGRP; 43017721Speter } 43117721Speter else if (p[0] == 'o') 43217721Speter { 43317721Speter if (can_read) 43417721Speter mode |= S_IROTH; 43517721Speter if (can_write) 43617721Speter mode |= S_IWOTH; 43717721Speter if (can_execute) 43817721Speter mode |= S_IXOTH; 43917721Speter } 44017721Speter } 44117721Speter /* Skip to the next field. */ 44217721Speter while (*p != ',' && *p != '\0') 44317721Speter ++p; 44417721Speter if (*p == ',') 44517721Speter ++p; 44617721Speter } 44717721Speter 44834461Speter if (respect_umask) 44934461Speter { 45034461Speter oumask = umask (0); 45134461Speter (void) umask (oumask); 45234461Speter mode &= ~oumask; 45334461Speter } 45434461Speter 45517721Speter if (chmod (filename, mode) < 0) 45617721Speter return errno; 45717721Speter return 0; 45817721Speter#endif /* ! CHMOD_BROKEN */ 45917721Speter} 46017721Speter 46117721Speter#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */ 46217721Speter 46317721Speter#ifdef CLIENT_SUPPORT 46417721Speter 46525839Speterint client_prune_dirs; 46617721Speter 46725839Speterstatic List *ignlist = (List *) NULL; 46817721Speter 46925839Speter/* Buffer to write to the server. */ 47025839Speterstatic struct buffer *to_server; 47117721Speter 47225839Speter/* Buffer used to read from the server. */ 47325839Speterstatic struct buffer *from_server; 47417721Speter 47525839Speter 47625839Speter/* We want to be able to log data sent between us and the server. We 47725839Speter do it using log buffers. Each log buffer has another buffer which 47825839Speter handles the actual I/O, and a file to log information to. 47917721Speter 48025839Speter This structure is the closure field of a log buffer. */ 48117721Speter 48225839Speterstruct log_buffer 48325839Speter{ 48425839Speter /* The underlying buffer. */ 48525839Speter struct buffer *buf; 48625839Speter /* The file to log information to. */ 48725839Speter FILE *log; 48825839Speter}; 48917721Speter 49025839Speterstatic struct buffer *log_buffer_initialize 49125839Speter PROTO((struct buffer *, FILE *, int, void (*) (struct buffer *))); 49225839Speterstatic int log_buffer_input PROTO((void *, char *, int, int, int *)); 49325839Speterstatic int log_buffer_output PROTO((void *, const char *, int, int *)); 49425839Speterstatic int log_buffer_flush PROTO((void *)); 49525839Speterstatic int log_buffer_block PROTO((void *, int)); 496102843Speterstatic int log_buffer_shutdown PROTO((struct buffer *)); 49717721Speter 49825839Speter/* Create a log buffer. */ 49917721Speter 50025839Speterstatic struct buffer * 50125839Speterlog_buffer_initialize (buf, fp, input, memory) 50225839Speter struct buffer *buf; 50325839Speter FILE *fp; 50425839Speter int input; 50525839Speter void (*memory) PROTO((struct buffer *)); 50625839Speter{ 50725839Speter struct log_buffer *n; 50817721Speter 50925839Speter n = (struct log_buffer *) xmalloc (sizeof *n); 51025839Speter n->buf = buf; 51125839Speter n->log = fp; 51225839Speter return buf_initialize (input ? log_buffer_input : NULL, 51325839Speter input ? NULL : log_buffer_output, 51425839Speter input ? NULL : log_buffer_flush, 51525839Speter log_buffer_block, 51625839Speter log_buffer_shutdown, 51725839Speter memory, 51825839Speter n); 51925839Speter} 52017721Speter 52125839Speter/* The input function for a log buffer. */ 52225839Speter 52325839Speterstatic int 52425839Speterlog_buffer_input (closure, data, need, size, got) 52525839Speter void *closure; 52625839Speter char *data; 52725839Speter int need; 52825839Speter int size; 52925839Speter int *got; 53025839Speter{ 53125839Speter struct log_buffer *lb = (struct log_buffer *) closure; 53225839Speter int status; 53325839Speter size_t n_to_write; 53425839Speter 53525839Speter if (lb->buf->input == NULL) 53625839Speter abort (); 53725839Speter 53825839Speter status = (*lb->buf->input) (lb->buf->closure, data, need, size, got); 53925839Speter if (status != 0) 54025839Speter return status; 54125839Speter 54225839Speter if (*got > 0) 54317721Speter { 54425839Speter n_to_write = *got; 54525839Speter if (fwrite (data, 1, n_to_write, lb->log) != n_to_write) 54625839Speter error (0, errno, "writing to log file"); 54717721Speter } 54825839Speter 54925839Speter return 0; 55025839Speter} 55125839Speter 55225839Speter/* The output function for a log buffer. */ 55325839Speter 55425839Speterstatic int 55525839Speterlog_buffer_output (closure, data, have, wrote) 55625839Speter void *closure; 55725839Speter const char *data; 55825839Speter int have; 55925839Speter int *wrote; 56025839Speter{ 56125839Speter struct log_buffer *lb = (struct log_buffer *) closure; 56225839Speter int status; 56325839Speter size_t n_to_write; 56425839Speter 56525839Speter if (lb->buf->output == NULL) 56625839Speter abort (); 56725839Speter 56825839Speter status = (*lb->buf->output) (lb->buf->closure, data, have, wrote); 56925839Speter if (status != 0) 57025839Speter return status; 57125839Speter 57225839Speter if (*wrote > 0) 57317721Speter { 57425839Speter n_to_write = *wrote; 57525839Speter if (fwrite (data, 1, n_to_write, lb->log) != n_to_write) 57625839Speter error (0, errno, "writing to log file"); 57717721Speter } 57825839Speter 57925839Speter return 0; 58017721Speter} 58125839Speter 58225839Speter/* The flush function for a log buffer. */ 58325839Speter 58425839Speterstatic int 58525839Speterlog_buffer_flush (closure) 58625839Speter void *closure; 58725839Speter{ 58825839Speter struct log_buffer *lb = (struct log_buffer *) closure; 58925839Speter 59025839Speter if (lb->buf->flush == NULL) 59125839Speter abort (); 59225839Speter 59325839Speter /* We don't really have to flush the log file here, but doing it 59425839Speter will let tail -f on the log file show what is sent to the 59525839Speter network as it is sent. */ 59625839Speter if (fflush (lb->log) != 0) 59725839Speter error (0, errno, "flushing log file"); 59825839Speter 59925839Speter return (*lb->buf->flush) (lb->buf->closure); 60025839Speter} 60125839Speter 60225839Speter/* The block function for a log buffer. */ 60325839Speter 60425839Speterstatic int 60525839Speterlog_buffer_block (closure, block) 60625839Speter void *closure; 60725839Speter int block; 60825839Speter{ 60925839Speter struct log_buffer *lb = (struct log_buffer *) closure; 61025839Speter 61125839Speter if (block) 61225839Speter return set_block (lb->buf); 61325839Speter else 61425839Speter return set_nonblock (lb->buf); 61525839Speter} 61625839Speter 61725839Speter/* The shutdown function for a log buffer. */ 61825839Speter 61925839Speterstatic int 620102843Speterlog_buffer_shutdown (buf) 621102843Speter struct buffer *buf; 62225839Speter{ 623102843Speter struct log_buffer *lb = (struct log_buffer *) buf->closure; 62432785Speter int retval; 62525839Speter 62632785Speter retval = buf_shutdown (lb->buf); 62732785Speter if (fclose (lb->log) < 0) 62832785Speter error (0, errno, "closing log file"); 62932785Speter return retval; 63025839Speter} 631102843Speter 63217721Speter#ifdef NO_SOCKET_TO_FD 63325839Speter 63417721Speter/* Under certain circumstances, we must communicate with the server 63517721Speter via a socket using send() and recv(). This is because under some 63617721Speter operating systems (OS/2 and Windows 95 come to mind), a socket 63717721Speter cannot be converted to a file descriptor -- it must be treated as a 63825839Speter socket and nothing else. 63925839Speter 64025839Speter We may also need to deal with socket routine error codes differently 64125839Speter in these cases. This is handled through the SOCK_ERRNO and 64225839Speter SOCK_STRERROR macros. */ 64325839Speter 64425839Speter/* These routines implement a buffer structure which uses send and 64525839Speter recv. The buffer is always in blocking mode so we don't implement 64625839Speter the block routine. */ 64717721Speter 64825839Speter/* Note that it is important that these routines always handle errors 64925839Speter internally and never return a positive errno code, since it would in 65025839Speter general be impossible for the caller to know in general whether any 65125839Speter error code came from a socket routine (to decide whether to use 65225839Speter SOCK_STRERROR or simply strerror to print an error message). */ 65317721Speter 65425839Speter/* We use an instance of this structure as the closure field. */ 65517721Speter 65625839Speterstruct socket_buffer 65725839Speter{ 65825839Speter /* The socket number. */ 65925839Speter int socket; 66025839Speter}; 66125839Speter 66225839Speterstatic struct buffer *socket_buffer_initialize 66325839Speter PROTO ((int, int, void (*) (struct buffer *))); 66425839Speterstatic int socket_buffer_input PROTO((void *, char *, int, int, int *)); 66525839Speterstatic int socket_buffer_output PROTO((void *, const char *, int, int *)); 66625839Speterstatic int socket_buffer_flush PROTO((void *)); 667102843Speterstatic int socket_buffer_shutdown PROTO((struct buffer *)); 66825839Speter 669102843Speter 670102843Speter 67125839Speter/* Create a buffer based on a socket. */ 67225839Speter 67325839Speterstatic struct buffer * 67425839Spetersocket_buffer_initialize (socket, input, memory) 675102843Speter int socket; 676102843Speter int input; 677102843Speter void (*memory) PROTO((struct buffer *)); 67825839Speter{ 67925839Speter struct socket_buffer *n; 68025839Speter 68125839Speter n = (struct socket_buffer *) xmalloc (sizeof *n); 68225839Speter n->socket = socket; 68325839Speter return buf_initialize (input ? socket_buffer_input : NULL, 68425839Speter input ? NULL : socket_buffer_output, 68525839Speter input ? NULL : socket_buffer_flush, 68625839Speter (int (*) PROTO((void *, int))) NULL, 687102843Speter socket_buffer_shutdown, 68825839Speter memory, 68925839Speter n); 69025839Speter} 69125839Speter 692102843Speter 693102843Speter 69425839Speter/* The buffer input function for a buffer built on a socket. */ 69525839Speter 69625839Speterstatic int 69725839Spetersocket_buffer_input (closure, data, need, size, got) 69825839Speter void *closure; 69925839Speter char *data; 70025839Speter int need; 70125839Speter int size; 70225839Speter int *got; 70325839Speter{ 70425839Speter struct socket_buffer *sb = (struct socket_buffer *) closure; 70525839Speter int nbytes; 70625839Speter 70725839Speter /* I believe that the recv function gives us exactly the semantics 70825839Speter we want. If there is a message, it returns immediately with 70925839Speter whatever it could get. If there is no message, it waits until 71025839Speter one comes in. In other words, it is not like read, which in 71125839Speter blocking mode normally waits until all the requested data is 71225839Speter available. */ 71325839Speter 71425839Speter *got = 0; 71525839Speter 71625839Speter do 71725839Speter { 71832785Speter 71932785Speter /* Note that for certain (broken?) networking stacks, like 72032785Speter VMS's UCX (not sure what version, problem reported with 72132785Speter recv() in 1997), and (according to windows-NT/config.h) 72232785Speter Windows NT 3.51, we must call recv or send with a 72332785Speter moderately sized buffer (say, less than 200K or something), 72432785Speter or else there may be network errors (somewhat hard to 72532785Speter produce, e.g. WAN not LAN or some such). buf_read_data 72632785Speter makes sure that we only recv() BUFFER_DATA_SIZE bytes at 72732785Speter a time. */ 72832785Speter 72925839Speter nbytes = recv (sb->socket, data, size, 0); 73025839Speter if (nbytes < 0) 73125839Speter error (1, 0, "reading from server: %s", SOCK_STRERROR (SOCK_ERRNO)); 73225839Speter if (nbytes == 0) 73325839Speter { 73425839Speter /* End of file (for example, the server has closed 73525839Speter the connection). If we've already read something, we 73625839Speter just tell the caller about the data, not about the end of 73725839Speter file. If we've read nothing, we return end of file. */ 73825839Speter if (*got == 0) 73925839Speter return -1; 74025839Speter else 74125839Speter return 0; 74225839Speter } 74325839Speter need -= nbytes; 74425839Speter size -= nbytes; 74525839Speter data += nbytes; 74625839Speter *got += nbytes; 74725839Speter } 74825839Speter while (need > 0); 74925839Speter 75025839Speter return 0; 75125839Speter} 75225839Speter 753102843Speter 754102843Speter 75525839Speter/* The buffer output function for a buffer built on a socket. */ 75625839Speter 75725839Speterstatic int 75825839Spetersocket_buffer_output (closure, data, have, wrote) 75925839Speter void *closure; 76025839Speter const char *data; 76125839Speter int have; 76225839Speter int *wrote; 76325839Speter{ 76425839Speter struct socket_buffer *sb = (struct socket_buffer *) closure; 76525839Speter 76625839Speter *wrote = have; 76725839Speter 76832785Speter /* See comment in socket_buffer_input regarding buffer size we pass 76932785Speter to send and recv. */ 77032785Speter 77125839Speter#ifdef SEND_NEVER_PARTIAL 77225839Speter /* If send() never will produce a partial write, then just do it. This 77325839Speter is needed for systems where its return value is something other than 77425839Speter the number of bytes written. */ 77525839Speter if (send (sb->socket, data, have, 0) < 0) 77625839Speter error (1, 0, "writing to server socket: %s", SOCK_STRERROR (SOCK_ERRNO)); 77725839Speter#else 77825839Speter while (have > 0) 77925839Speter { 78025839Speter int nbytes; 78125839Speter 78225839Speter nbytes = send (sb->socket, data, have, 0); 78325839Speter if (nbytes < 0) 78425839Speter error (1, 0, "writing to server socket: %s", SOCK_STRERROR (SOCK_ERRNO)); 78525839Speter 78625839Speter have -= nbytes; 78725839Speter data += nbytes; 78825839Speter } 78925839Speter#endif 79025839Speter 79125839Speter return 0; 79225839Speter} 79325839Speter 794102843Speter 795102843Speter 79625839Speter/* The buffer flush function for a buffer built on a socket. */ 79725839Speter 79825839Speter/*ARGSUSED*/ 79925839Speterstatic int 80025839Spetersocket_buffer_flush (closure) 80125839Speter void *closure; 80225839Speter{ 80325839Speter /* Nothing to do. Sockets are always flushed. */ 80425839Speter return 0; 80525839Speter} 80625839Speter 807102843Speter 808102843Speter 809102843Speterstatic int 810102843Spetersocket_buffer_shutdown (buf) 811102843Speter struct buffer *buf; 812102843Speter{ 813102843Speter struct socket_buffer *n = (struct socket_buffer *) buf->closure; 814102843Speter char tmp; 815102843Speter 816102843Speter /* no need to flush children of an endpoint buffer here */ 817102843Speter 818102843Speter if (buf->input) 819102843Speter { 820102843Speter int err = 0; 821102843Speter if (! buf_empty_p (buf) 822102843Speter || (err = recv (n->socket, &tmp, 1, 0)) > 0) 823102843Speter error (0, 0, "dying gasps from %s unexpected", current_parsed_root->hostname); 824102843Speter else if (err == -1) 825102843Speter error (0, 0, "reading from %s: %s", current_parsed_root->hostname, SOCK_STRERROR (SOCK_ERRNO)); 826102843Speter 827102843Speter /* shutdown() socket */ 828102843Speter# ifdef SHUTDOWN_SERVER 829102843Speter if (current_parsed_root->method != server_method) 830102843Speter# endif 831102843Speter if (shutdown (n->socket, 0) < 0) 832102843Speter { 833102843Speter error (1, 0, "shutting down server socket: %s", SOCK_STRERROR (SOCK_ERRNO)); 834102843Speter } 835102843Speter 836102843Speter buf->input = NULL; 837102843Speter } 838102843Speter else if (buf->output) 839102843Speter { 840102843Speter /* shutdown() socket */ 841102843Speter# ifdef SHUTDOWN_SERVER 842102843Speter /* FIXME: Should have a SHUTDOWN_SERVER_INPUT & 843102843Speter * SHUTDOWN_SERVER_OUTPUT 844102843Speter */ 845102843Speter if (current_parsed_root->method == server_method) 846102843Speter SHUTDOWN_SERVER (n->socket); 847102843Speter else 848102843Speter# endif 849102843Speter if (shutdown (n->socket, 1) < 0) 850102843Speter { 851102843Speter error (1, 0, "shutting down server socket: %s", SOCK_STRERROR (SOCK_ERRNO)); 852102843Speter } 853102843Speter 854102843Speter buf->output = NULL; 855102843Speter } 856102843Speter 857102843Speter return 0; 858102843Speter} 859102843Speter 86025839Speter#endif /* NO_SOCKET_TO_FD */ 861102843Speter 86217721Speter/* 86317721Speter * Read a line from the server. Result does not include the terminating \n. 86417721Speter * 86517721Speter * Space for the result is malloc'd and should be freed by the caller. 86617721Speter * 86725839Speter * Returns number of bytes read. 86817721Speter */ 86917721Speterstatic int 87025839Speterread_line (resultp) 87117721Speter char **resultp; 87217721Speter{ 87325839Speter int status; 87417721Speter char *result; 87525839Speter int len; 87617721Speter 87725839Speter status = buf_flush (to_server, 1); 87825839Speter if (status != 0) 87925839Speter error (1, status, "writing to server"); 88017721Speter 88125839Speter status = buf_read_line (from_server, &result, &len); 88225839Speter if (status != 0) 88317721Speter { 88425839Speter if (status == -1) 88525839Speter error (1, 0, "end of file from server (consult above messages if any)"); 88625839Speter else if (status == -2) 88725839Speter error (1, 0, "out of memory"); 88825839Speter else 88925839Speter error (1, status, "reading from server"); 89017721Speter } 89117721Speter 89225839Speter if (resultp != NULL) 89317721Speter *resultp = result; 89425839Speter else 89525839Speter free (result); 89617721Speter 89725839Speter return len; 89817721Speter} 89917721Speter 90017721Speter#endif /* CLIENT_SUPPORT */ 90117721Speter 90217721Speter 90317721Speter#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT) 90417721Speter 90517721Speter/* 90625839Speter * Level of compression to use when running gzip on a single file. 90725839Speter */ 90825839Speterint file_gzip_level; 90925839Speter 91017721Speter#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */ 91117721Speter 91217721Speter#ifdef CLIENT_SUPPORT 91317721Speter 91417721Speter/* 91517721Speter * The Repository for the top level of this command (not necessarily 91617721Speter * the CVSROOT, just the current directory at the time we do it). 91717721Speter */ 91854427Speterstatic char *toplevel_repos = NULL; 91917721Speter 92025839Speter/* Working directory when we first started. Note: we could speed things 92125839Speter up on some systems by using savecwd.h here instead of just always 92225839Speter storing a name. */ 92325839Speterchar *toplevel_wd; 92417721Speter 92517721Speterstatic void 92617721Speterhandle_ok (args, len) 92717721Speter char *args; 92817721Speter int len; 92917721Speter{ 93017721Speter return; 93117721Speter} 93217721Speter 93317721Speterstatic void 93417721Speterhandle_error (args, len) 93517721Speter char *args; 93617721Speter int len; 93717721Speter{ 93817721Speter int something_printed; 93917721Speter 94017721Speter /* 94117721Speter * First there is a symbolic error code followed by a space, which 94217721Speter * we ignore. 94317721Speter */ 94417721Speter char *p = strchr (args, ' '); 94517721Speter if (p == NULL) 94617721Speter { 94717721Speter error (0, 0, "invalid data from cvs server"); 94817721Speter return; 94917721Speter } 95017721Speter ++p; 95154427Speter 95254427Speter /* Next we print the text of the message from the server. We 95354427Speter probably should be prefixing it with "server error" or some 95454427Speter such, because if it is something like "Out of memory", the 95554427Speter current behavior doesn't say which machine is out of 95654427Speter memory. */ 95754427Speter 95817721Speter len -= p - args; 95917721Speter something_printed = 0; 96017721Speter for (; len > 0; --len) 96117721Speter { 96217721Speter something_printed = 1; 96317721Speter putc (*p++, stderr); 96417721Speter } 96517721Speter if (something_printed) 96617721Speter putc ('\n', stderr); 96717721Speter} 96817721Speter 96917721Speterstatic void 97017721Speterhandle_valid_requests (args, len) 97117721Speter char *args; 97217721Speter int len; 97317721Speter{ 97417721Speter char *p = args; 97517721Speter char *q; 97617721Speter struct request *rq; 97717721Speter do 97817721Speter { 97917721Speter q = strchr (p, ' '); 98017721Speter if (q != NULL) 98117721Speter *q++ = '\0'; 98217721Speter for (rq = requests; rq->name != NULL; ++rq) 98317721Speter { 98417721Speter if (strcmp (rq->name, p) == 0) 98517721Speter break; 98617721Speter } 98717721Speter if (rq->name == NULL) 98817721Speter /* 98917721Speter * It is a request we have never heard of (and thus never 99017721Speter * will want to use). So don't worry about it. 99117721Speter */ 99217721Speter ; 99317721Speter else 99417721Speter { 99554427Speter if (rq->flags & RQ_ENABLEME) 99617721Speter { 99717721Speter /* 99817721Speter * Server wants to know if we have this, to enable the 99917721Speter * feature. 100017721Speter */ 100117721Speter send_to_server (rq->name, 0); 100217721Speter send_to_server ("\012", 0); 100317721Speter } 100417721Speter else 100554427Speter rq->flags |= RQ_SUPPORTED; 100617721Speter } 100717721Speter p = q; 100817721Speter } while (q != NULL); 100917721Speter for (rq = requests; rq->name != NULL; ++rq) 101017721Speter { 101154427Speter if ((rq->flags & RQ_SUPPORTED) 101254427Speter || (rq->flags & RQ_ENABLEME)) 101354427Speter continue; 101454427Speter if (rq->flags & RQ_ESSENTIAL) 101517721Speter error (1, 0, "request `%s' not supported by server", rq->name); 101617721Speter } 101717721Speter} 101817721Speter 101917721Speter 1020128269Speter 102117721Speter/* 1022128269Speter * This is a proc for walklist(). It inverts the error return premise of 1023128269Speter * walklist. 1024128269Speter * 1025128269Speter * RETURNS 1026128269Speter * True If this path is prefixed by one of the paths in walklist and 1027128269Speter * does not step above the prefix path. 1028128269Speter * False Otherwise. 1029128269Speter */ 1030128269Speterstatic 1031128269Speterint path_list_prefixed (p, closure) 1032128269Speter Node *p; 1033128269Speter void *closure; 1034128269Speter{ 1035128269Speter const char *questionable = closure; 1036128269Speter const char *prefix = p->key; 1037128269Speter if (strncmp (prefix, questionable, strlen (prefix))) return 0; 1038128269Speter questionable += strlen (prefix); 1039128269Speter while (ISDIRSEP (*questionable)) questionable++; 1040128269Speter if (*questionable == '\0') return 1; 1041128269Speter return pathname_levels (questionable); 1042128269Speter} 1043128269Speter 1044128269Speter 1045128269Speter 1046128269Speter/* 1047128269Speter * Need to validate the client pathname. Disallowed paths include: 1048128269Speter * 1049128269Speter * 1. Absolute paths. 1050128269Speter * 2. Pathnames that do not reference a specifically requested update 1051128269Speter * directory. 1052128269Speter * 1053128269Speter * In case 2, we actually only check that the directory is under the uppermost 1054128269Speter * directories mentioned on the command line. 1055128269Speter * 1056128269Speter * RETURNS 1057128269Speter * True If the path is valid. 1058128269Speter * False Otherwise. 1059128269Speter */ 1060128269Speterstatic 1061128269Speterint is_valid_client_path (pathname) 1062128269Speter const char *pathname; 1063128269Speter{ 1064128269Speter /* 1. Absolute paths. */ 1065128269Speter if (isabsolute (pathname)) return 0; 1066128269Speter /* 2. No up-references in path. */ 1067128269Speter if (pathname_levels (pathname) == 0) return 1; 1068128269Speter /* 2. No Max-dotdot paths registered. */ 1069128269Speter if (uppaths == NULL) return 0; 1070128269Speter 1071128269Speter return walklist (uppaths, path_list_prefixed, (void *)pathname); 1072128269Speter} 1073128269Speter 1074128269Speter 1075128269Speter 1076128269Speter/* 107717721Speter * Do all the processing for PATHNAME, where pathname consists of the 107817721Speter * repository and the filename. The parameters we pass to FUNC are: 107917721Speter * DATA is just the DATA parameter which was passed to 108017721Speter * call_in_directory; ENT_LIST is a pointer to an entries list (which 108117721Speter * we manage the storage for); SHORT_PATHNAME is the pathname of the 108217721Speter * file relative to the (overall) directory in which the command is 108317721Speter * taking place; and FILENAME is the filename portion only of 108417721Speter * SHORT_PATHNAME. When we call FUNC, the curent directory points to 108517721Speter * the directory portion of SHORT_PATHNAME. */ 108617721Speter 108717721Speterstatic void 108817721Spetercall_in_directory (pathname, func, data) 108917721Speter char *pathname; 109017721Speter void (*func) PROTO((char *data, List *ent_list, char *short_pathname, 109117721Speter char *filename)); 109217721Speter char *data; 109317721Speter{ 1094128269Speter /* This variable holds the result of Entries_Open. */ 1095128269Speter List *last_entries = NULL; 109617721Speter char *dir_name; 109717721Speter char *filename; 109825839Speter /* This is what we get when we hook up the directory (working directory 109925839Speter name) from PATHNAME with the filename from REPOSNAME. For example: 110025839Speter pathname: ccvs/src/ 110125839Speter reposname: /u/src/master/ccvs/foo/ChangeLog 110225839Speter short_pathname: ccvs/src/ChangeLog 110325839Speter */ 110425839Speter char *short_pathname; 110517721Speter char *p; 110617721Speter 110717721Speter /* 110817721Speter * Do the whole descent in parallel for the repositories, so we 110917721Speter * know what to put in CVS/Repository files. I'm not sure the 111017721Speter * full hair is necessary since the server does a similar 111117721Speter * computation; I suspect that we only end up creating one 111217721Speter * directory at a time anyway. 111317721Speter * 111417721Speter * Also note that we must *only* worry about this stuff when we 111517721Speter * are creating directories; `cvs co foo/bar; cd foo/bar; cvs co 111617721Speter * CVSROOT; cvs update' is legitimate, but in this case 111717721Speter * foo/bar/CVSROOT/CVS/Repository is not a subdirectory of 111817721Speter * foo/bar/CVS/Repository. 111917721Speter */ 112017721Speter char *reposname; 112117721Speter char *short_repos; 112217721Speter char *reposdirname; 112317721Speter char *rdirp; 112417721Speter int reposdirname_absolute; 1125128269Speter int newdir = 0; 112617721Speter 1127175266Sobrien assert (pathname); 1128175266Sobrien 112917721Speter reposname = NULL; 113025839Speter read_line (&reposname); 113125839Speter assert (reposname != NULL); 113217721Speter 113317721Speter reposdirname_absolute = 0; 113425839Speter if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)) != 0) 113517721Speter { 113625839Speter reposdirname_absolute = 1; 113725839Speter short_repos = reposname; 113825839Speter } 113925839Speter else 114025839Speter { 114125839Speter short_repos = reposname + strlen (toplevel_repos) + 1; 114225839Speter if (short_repos[-1] != '/') 114317721Speter { 114417721Speter reposdirname_absolute = 1; 114517721Speter short_repos = reposname; 114617721Speter } 114717721Speter } 1148128269Speter 1149128269Speter /* Now that we have SHORT_REPOS, we can calculate the path to the file we 1150128269Speter * are being requested to operate on. 1151128269Speter */ 1152128269Speter filename = strrchr (short_repos, '/'); 1153128269Speter if (filename == NULL) 1154128269Speter filename = short_repos; 1155128269Speter else 1156128269Speter ++filename; 1157128269Speter 1158128269Speter short_pathname = xmalloc (strlen (pathname) + strlen (filename) + 5); 1159128269Speter strcpy (short_pathname, pathname); 1160128269Speter strcat (short_pathname, filename); 1161128269Speter 1162128269Speter /* Now that we know the path to the file we were requested to operate on, 1163128269Speter * we can verify that it is valid. 1164128269Speter * 1165128269Speter * For security reasons, if SHORT_PATHNAME is absolute or attempts to 1166128269Speter * ascend outside of the current sanbbox, we abort. The server should not 1167128269Speter * send us anything but relative paths which remain inside the sandbox 1168128269Speter * here. Anything less means a trojan CVS server could create and edit 1169128269Speter * arbitrary files on the client. 1170128269Speter */ 1171128269Speter if (!is_valid_client_path (short_pathname)) 1172128269Speter { 1173128269Speter error (0, 0, 1174128269Speter "Server attempted to update a file via an invalid pathname:"); 1175128269Speter error (1, 0, "`%s'.", short_pathname); 1176128269Speter } 1177128269Speter 117817721Speter reposdirname = xstrdup (short_repos); 117917721Speter p = strrchr (reposdirname, '/'); 118017721Speter if (p == NULL) 118117721Speter { 118217721Speter reposdirname = xrealloc (reposdirname, 2); 118317721Speter reposdirname[0] = '.'; reposdirname[1] = '\0'; 118417721Speter } 118517721Speter else 118617721Speter *p = '\0'; 118717721Speter 118825839Speter dir_name = xstrdup (pathname); 118917721Speter p = strrchr (dir_name, '/'); 119017721Speter if (p == NULL) 119117721Speter { 119217721Speter dir_name = xrealloc (dir_name, 2); 119317721Speter dir_name[0] = '.'; dir_name[1] = '\0'; 119417721Speter } 119517721Speter else 119617721Speter *p = '\0'; 119717721Speter if (client_prune_dirs) 119817721Speter add_prune_candidate (dir_name); 119917721Speter 1200128269Speter if (toplevel_wd == NULL) 1201128269Speter { 1202128269Speter toplevel_wd = xgetwd (); 1203128269Speter if (toplevel_wd == NULL) 1204128269Speter error (1, errno, "could not get working directory"); 1205128269Speter } 120617721Speter 1207128269Speter if (CVS_CHDIR (toplevel_wd) < 0) 1208128269Speter error (1, errno, "could not chdir to %s", toplevel_wd); 120917721Speter 1210128269Speter if (CVS_CHDIR (dir_name) < 0) 1211128269Speter { 1212128269Speter char *dir; 1213128269Speter char *dirp; 1214128269Speter 1215128269Speter if (! existence_error (errno)) 1216128269Speter error (1, errno, "could not chdir to %s", dir_name); 1217128269Speter 1218128269Speter /* Directory does not exist, we need to create it. */ 1219128269Speter newdir = 1; 122025839Speter 1221128269Speter /* Provided we are willing to assume that directories get 1222128269Speter created one at a time, we could simplify this a lot. 1223128269Speter Do note that one aspect still would need to walk the 1224128269Speter dir_name path: the checking for "fncmp (dir, CVSADM)". */ 122525839Speter 1226128269Speter dir = xmalloc (strlen (dir_name) + 1); 1227128269Speter dirp = dir_name; 1228128269Speter rdirp = reposdirname; 122925839Speter 1230128269Speter /* This algorithm makes nested directories one at a time 1231128269Speter and create CVS administration files in them. For 1232128269Speter example, we're checking out foo/bar/baz from the 1233128269Speter repository: 123425839Speter 1235128269Speter 1) create foo, point CVS/Repository to <root>/foo 1236128269Speter 2) .. foo/bar .. <root>/foo/bar 1237128269Speter 3) .. foo/bar/baz .. <root>/foo/bar/baz 1238128269Speter 1239128269Speter As you can see, we're just stepping along DIR_NAME (with 1240128269Speter DIRP) and REPOSDIRNAME (with RDIRP) respectively. 124125839Speter 1242128269Speter We need to be careful when we are checking out a 1243128269Speter module, however, since DIR_NAME and REPOSDIRNAME are not 1244128269Speter going to be the same. Since modules will not have any 1245128269Speter slashes in their names, we should watch the output of 1246128269Speter STRCHR to decide whether or not we should use STRCHR on 1247128269Speter the RDIRP. That is, if we're down to a module name, 1248128269Speter don't keep picking apart the repository directory name. */ 1249128269Speter 1250128269Speter do 125125839Speter { 1252128269Speter dirp = strchr (dirp, '/'); 1253128269Speter if (dirp) 1254128269Speter { 1255128269Speter strncpy (dir, dir_name, dirp - dir_name); 1256128269Speter dir[dirp - dir_name] = '\0'; 1257128269Speter /* Skip the slash. */ 1258128269Speter ++dirp; 1259128269Speter if (rdirp == NULL) 1260128269Speter /* This just means that the repository string has 1261128269Speter fewer components than the dir_name string. But 1262128269Speter that is OK (e.g. see modules3-8 in testsuite). */ 1263128269Speter ; 1264128269Speter else 1265128269Speter rdirp = strchr (rdirp, '/'); 1266128269Speter } 1267128269Speter else 1268128269Speter { 1269128269Speter /* If there are no more slashes in the dir name, 1270128269Speter we're down to the most nested directory -OR- to 1271128269Speter the name of a module. In the first case, we 1272128269Speter should be down to a DIRP that has no slashes, 1273128269Speter so it won't help/hurt to do another STRCHR call 1274128269Speter on DIRP. It will definitely hurt, however, if 1275128269Speter we're down to a module name, since a module 1276128269Speter name can point to a nested directory (that is, 1277128269Speter DIRP will still have slashes in it. Therefore, 1278128269Speter we should set it to NULL so the routine below 1279128269Speter copies the contents of REMOTEDIRNAME onto the 1280128269Speter root repository directory (does this if rdirp 1281128269Speter is set to NULL, because we used to do an extra 1282128269Speter STRCHR call here). */ 128325839Speter 1284128269Speter rdirp = NULL; 1285128269Speter strcpy (dir, dir_name); 1286128269Speter } 128725839Speter 1288128269Speter if (fncmp (dir, CVSADM) == 0) 1289128269Speter { 1290128269Speter error (0, 0, "cannot create a directory named %s", dir); 1291128269Speter error (0, 0, "because CVS uses \"%s\" for its own uses", 1292128269Speter CVSADM); 1293128269Speter error (1, 0, "rename the directory and try again"); 1294128269Speter } 129517721Speter 1296128269Speter if (mkdir_if_needed (dir)) 1297128269Speter { 1298128269Speter /* It already existed, fine. Just keep going. */ 1299128269Speter } 1300128269Speter else if (strcmp (cvs_cmd_name, "export") == 0) 1301128269Speter /* Don't create CVSADM directories if this is export. */ 1302128269Speter ; 1303128269Speter else 1304128269Speter { 1305128269Speter /* 1306128269Speter * Put repository in CVS/Repository. For historical 1307128269Speter * (pre-CVS/Root) reasons, this is an absolute pathname, 1308128269Speter * but what really matters is the part of it which is 1309128269Speter * relative to cvsroot. 1310128269Speter */ 1311128269Speter char *repo; 1312128269Speter char *r, *b; 131317721Speter 1314128269Speter repo = xmalloc (strlen (reposdirname) 1315128269Speter + strlen (toplevel_repos) 1316128269Speter + 80); 1317128269Speter if (reposdirname_absolute) 1318128269Speter r = repo; 131917721Speter else 132025839Speter { 1321128269Speter strcpy (repo, toplevel_repos); 1322128269Speter strcat (repo, "/"); 1323128269Speter r = repo + strlen (repo); 132425839Speter } 132517721Speter 1326128269Speter if (rdirp) 132717721Speter { 1328128269Speter /* See comment near start of function; the only 1329128269Speter way that the server can put the right thing 1330128269Speter in each CVS/Repository file is to create the 1331128269Speter directories one at a time. I think that the 1332128269Speter CVS server has been doing this all along. */ 1333128269Speter error (0, 0, "\ 1334128269Speterwarning: server is not creating directories one at a time"); 1335128269Speter strncpy (r, reposdirname, rdirp - reposdirname); 1336128269Speter r[rdirp - reposdirname] = '\0'; 133717721Speter } 133817721Speter else 1339128269Speter strcpy (r, reposdirname); 134017721Speter 1341128269Speter Create_Admin (dir, dir, repo, 1342128269Speter (char *)NULL, (char *)NULL, 0, 0, 1); 1343128269Speter free (repo); 134417721Speter 1345128269Speter b = strrchr (dir, '/'); 1346128269Speter if (b == NULL) 1347128269Speter Subdir_Register ((List *) NULL, (char *) NULL, dir); 1348128269Speter else 134917721Speter { 1350128269Speter *b = '\0'; 1351128269Speter Subdir_Register ((List *) NULL, dir, b + 1); 1352128269Speter *b = '/'; 135317721Speter } 1354128269Speter } 135517721Speter 1356128269Speter if (rdirp != NULL) 135781407Speter { 1358128269Speter /* Skip the slash. */ 1359128269Speter ++rdirp; 136081407Speter } 136181407Speter 1362128269Speter } while (dirp != NULL); 1363128269Speter free (dir); 1364128269Speter /* Now it better work. */ 1365128269Speter if ( CVS_CHDIR (dir_name) < 0) 1366128269Speter error (1, errno, "could not chdir to %s", dir_name); 1367128269Speter } 1368128269Speter else if (strcmp (cvs_cmd_name, "export") == 0) 1369128269Speter /* Don't create CVSADM directories if this is export. */ 1370128269Speter ; 1371128269Speter else if (!isdir (CVSADM)) 1372128269Speter { 1373128269Speter /* 1374128269Speter * Put repository in CVS/Repository. For historical 1375128269Speter * (pre-CVS/Root) reasons, this is an absolute pathname, 1376128269Speter * but what really matters is the part of it which is 1377128269Speter * relative to cvsroot. 1378128269Speter */ 1379128269Speter char *repo; 1380128269Speter 1381128269Speter if (reposdirname_absolute) 1382128269Speter repo = reposdirname; 1383128269Speter else 1384128269Speter { 1385128269Speter repo = xmalloc (strlen (reposdirname) 1386128269Speter + strlen (toplevel_repos) 1387128269Speter + 10); 1388128269Speter strcpy (repo, toplevel_repos); 1389128269Speter strcat (repo, "/"); 1390128269Speter strcat (repo, reposdirname); 139181407Speter } 139281407Speter 1393128269Speter Create_Admin (".", ".", repo, (char *)NULL, (char *)NULL, 0, 1, 1); 1394128269Speter if (repo != reposdirname) 1395128269Speter free (repo); 1396128269Speter } 1397128269Speter 1398128269Speter if (strcmp (cvs_cmd_name, "export") != 0) 1399128269Speter { 1400128269Speter last_entries = Entries_Open (0, dir_name); 1401128269Speter 1402128269Speter /* If this is a newly created directory, we will record 1403128269Speter all subdirectory information, so call Subdirs_Known in 1404128269Speter case there are no subdirectories. If this is not a 1405128269Speter newly created directory, it may be an old working 1406128269Speter directory from before we recorded subdirectory 1407128269Speter information in the Entries file. We force a search for 1408128269Speter all subdirectories now, to make sure our subdirectory 1409128269Speter information is up to date. If the Entries file does 1410128269Speter record subdirectory information, then this call only 1411128269Speter does list manipulation. */ 1412128269Speter if (newdir) 1413128269Speter Subdirs_Known (last_entries); 1414128269Speter else 141517721Speter { 1416128269Speter List *dirlist; 141725839Speter 1418128269Speter dirlist = Find_Directories ((char *) NULL, W_LOCAL, 1419128269Speter last_entries); 1420128269Speter dellist (&dirlist); 142117721Speter } 142217721Speter } 142317721Speter free (reposdirname); 142417721Speter (*func) (data, last_entries, short_pathname, filename); 1425128269Speter if (last_entries != NULL) 1426128269Speter Entries_Close (last_entries); 1427128269Speter free (dir_name); 142825839Speter free (short_pathname); 142925839Speter free (reposname); 143017721Speter} 143117721Speter 143217721Speterstatic void 143317721Spetercopy_a_file (data, ent_list, short_pathname, filename) 143417721Speter char *data; 143517721Speter List *ent_list; 143617721Speter char *short_pathname; 143717721Speter char *filename; 143817721Speter{ 143917721Speter char *newname; 144017721Speter#ifdef USE_VMS_FILENAMES 144117721Speter char *p; 144217721Speter#endif 144317721Speter 144425839Speter read_line (&newname); 144517721Speter 144617721Speter#ifdef USE_VMS_FILENAMES 144717721Speter /* Mogrify the filename so VMS is happy with it. */ 144817721Speter for(p = newname; *p; p++) 144917721Speter if(*p == '.' || *p == '#') *p = '_'; 145017721Speter#endif 145154427Speter /* cvsclient.texi has said for a long time that newname must be in the 145254427Speter same directory. Wouldn't want a malicious or buggy server overwriting 145354427Speter ~/.profile, /etc/passwd, or anything like that. */ 145454427Speter if (last_component (newname) != newname) 145554427Speter error (1, 0, "protocol error: Copy-file tried to specify directory"); 145617721Speter 145754427Speter if (unlink_file (newname) && !existence_error (errno)) 145854427Speter error (0, errno, "unable to remove %s", newname); 145917721Speter copy_file (filename, newname); 146017721Speter free (newname); 146117721Speter} 146217721Speter 146317721Speterstatic void 146417721Speterhandle_copy_file (args, len) 146517721Speter char *args; 146617721Speter int len; 146717721Speter{ 146817721Speter call_in_directory (args, copy_a_file, (char *)NULL); 146917721Speter} 147017721Speter 1471175266Sobrien 1472175266Sobrien 1473175266Sobrien/* Attempt to read a file size from a string. Accepts base 8 (0N), base 16 1474175266Sobrien * (0xN), or base 10. Exits on error. 1475175266Sobrien * 1476175266Sobrien * RETURNS 1477175266Sobrien * The file size, in a size_t. 1478175266Sobrien * 1479175266Sobrien * FATAL ERRORS 1480175266Sobrien * 1. As strtoul(). 1481175266Sobrien * 2. If the number read exceeds SIZE_MAX. 1482175266Sobrien */ 1483175266Sobrienstatic size_t 1484175266Sobrienstrto_file_size (const char *s) 1485175266Sobrien{ 1486175266Sobrien unsigned long tmp; 1487175266Sobrien char *endptr; 1488175266Sobrien 1489175266Sobrien /* Read it. */ 1490175266Sobrien errno = 0; 1491175266Sobrien tmp = strtoul (s, &endptr, 0); 1492175266Sobrien 1493175266Sobrien /* Check for errors. */ 1494175266Sobrien if (errno || endptr == s) 1495175266Sobrien error (1, errno, "Server sent invalid file size `%s'", s); 1496175266Sobrien if (*endptr != '\0') 1497175266Sobrien error (1, 0, 1498175266Sobrien "Server sent trailing characters in file size `%s'", 1499175266Sobrien endptr); 1500175266Sobrien if (tmp > SIZE_MAX) 1501175266Sobrien error (1, 0, "Server sent file size exceeding client max."); 1502175266Sobrien 1503175266Sobrien /* Return it. */ 1504175266Sobrien return (size_t)tmp; 1505175266Sobrien} 1506175266Sobrien 1507175266Sobrien 1508175266Sobrien 150917721Speterstatic void read_counted_file PROTO ((char *, char *)); 151017721Speter 151117721Speter/* Read from the server the count for the length of a file, then read 151217721Speter the contents of that file and write them to FILENAME. FULLNAME is 151317721Speter the name of the file for use in error messages. FIXME-someday: 151417721Speter extend this to deal with compressed files and make update_entries 151517721Speter use it. On error, gives a fatal error. */ 151617721Speterstatic void 151717721Speterread_counted_file (filename, fullname) 151817721Speter char *filename; 151917721Speter char *fullname; 152017721Speter{ 152117721Speter char *size_string; 152217721Speter size_t size; 152317721Speter char *buf; 152417721Speter 152517721Speter /* Pointers in buf to the place to put data which will be read, 152617721Speter and the data which needs to be written, respectively. */ 152717721Speter char *pread; 152817721Speter char *pwrite; 152917721Speter /* Number of bytes left to read and number of bytes in buf waiting to 153017721Speter be written, respectively. */ 153117721Speter size_t nread; 153217721Speter size_t nwrite; 153317721Speter 153417721Speter FILE *fp; 153517721Speter 153625839Speter read_line (&size_string); 153717721Speter if (size_string[0] == 'z') 153817721Speter error (1, 0, "\ 153917721Speterprotocol error: compressed files not supported for that operation"); 1540175266Sobrien size = strto_file_size (size_string); 154117721Speter free (size_string); 154217721Speter 154317721Speter /* A more sophisticated implementation would use only a limited amount 154417721Speter of buffer space (8K perhaps), and read that much at a time. We allocate 154517721Speter a buffer for the whole file only to make it easy to keep track what 154617721Speter needs to be read and written. */ 154717721Speter buf = xmalloc (size); 154817721Speter 154917721Speter /* FIXME-someday: caller should pass in a flag saying whether it 155017721Speter is binary or not. I haven't carefully looked into whether 155117721Speter CVS/Template files should use local text file conventions or 155217721Speter not. */ 155325839Speter fp = CVS_FOPEN (filename, "wb"); 155417721Speter if (fp == NULL) 155517721Speter error (1, errno, "cannot write %s", fullname); 155617721Speter nread = size; 155717721Speter nwrite = 0; 155817721Speter pread = buf; 155917721Speter pwrite = buf; 156017721Speter while (nread > 0 || nwrite > 0) 156117721Speter { 156217721Speter size_t n; 156317721Speter 156417721Speter if (nread > 0) 156517721Speter { 156617721Speter n = try_read_from_server (pread, nread); 156717721Speter nread -= n; 156817721Speter pread += n; 156917721Speter nwrite += n; 157017721Speter } 157117721Speter 157217721Speter if (nwrite > 0) 157317721Speter { 157417721Speter n = fwrite (pwrite, 1, nwrite, fp); 157517721Speter if (ferror (fp)) 157617721Speter error (1, errno, "cannot write %s", fullname); 157717721Speter nwrite -= n; 157817721Speter pwrite += n; 157917721Speter } 158017721Speter } 158117721Speter free (buf); 158217721Speter if (fclose (fp) < 0) 158317721Speter error (1, errno, "cannot close %s", fullname); 158417721Speter} 158517721Speter 158632785Speter/* OK, we want to swallow the "U foo.c" response and then output it only 158732785Speter if we can update the file. In the future we probably want some more 158832785Speter systematic approach to parsing tagged text, but for now we keep it 158932785Speter ad hoc. "Why," I hear you cry, "do we not just look at the 159032785Speter Update-existing and Created responses?" That is an excellent question, 159132785Speter and the answer is roughly conservatism/laziness--I haven't read through 159232785Speter update.c enough to figure out the exact correspondence or lack thereof 159332785Speter between those responses and a "U foo.c" line (note that Merged, from 159432785Speter join_file, can be either "C foo" or "U foo" depending on the context). */ 159532785Speter/* Nonzero if we have seen +updated and not -updated. */ 159632785Speterstatic int updated_seen; 159732785Speter/* Filename from an "fname" tagged response within +updated/-updated. */ 159832785Speterstatic char *updated_fname; 159932785Speter 160054427Speter/* This struct is used to hold data when reading the +importmergecmd 160154427Speter and -importmergecmd tags. We put the variables in a struct only 160254427Speter for namespace issues. FIXME: As noted above, we need to develop a 160354427Speter more systematic approach. */ 160454427Speterstatic struct 160554427Speter{ 160654427Speter /* Nonzero if we have seen +importmergecmd and not -importmergecmd. */ 160754427Speter int seen; 160854427Speter /* Number of conflicts, from a "conflicts" tagged response. */ 160954427Speter int conflicts; 161054427Speter /* First merge tag, from a "mergetag1" tagged response. */ 161154427Speter char *mergetag1; 161254427Speter /* Second merge tag, from a "mergetag2" tagged response. */ 161354427Speter char *mergetag2; 161454427Speter /* Repository, from a "repository" tagged response. */ 161554427Speter char *repository; 161654427Speter} importmergecmd; 161732785Speter 161832785Speter/* Nonzero if we should arrange to return with a failure exit status. */ 161932785Speterstatic int failure_exit; 162032785Speter 162132785Speter 162217721Speter/* 162325839Speter * The time stamp of the last file we registered. 162425839Speter */ 162525839Speterstatic time_t last_register_time; 162625839Speter 162725839Speter/* 162817721Speter * The Checksum response gives the checksum for the file transferred 162917721Speter * over by the next Updated, Merged or Patch response. We just store 163017721Speter * it here, and then check it in update_entries. 163117721Speter */ 163217721Speter 163317721Speterstatic int stored_checksum_valid; 163417721Speterstatic unsigned char stored_checksum[16]; 163517721Speter 163617721Speterstatic void 163717721Speterhandle_checksum (args, len) 163817721Speter char *args; 163917721Speter int len; 164017721Speter{ 164117721Speter char *s; 164217721Speter char buf[3]; 164317721Speter int i; 164417721Speter 164517721Speter if (stored_checksum_valid) 164617721Speter error (1, 0, "Checksum received before last one was used"); 164717721Speter 164817721Speter s = args; 164917721Speter buf[2] = '\0'; 165017721Speter for (i = 0; i < 16; i++) 165117721Speter { 165217721Speter char *bufend; 165317721Speter 165417721Speter buf[0] = *s++; 165517721Speter buf[1] = *s++; 165617721Speter stored_checksum[i] = (char) strtol (buf, &bufend, 16); 165717721Speter if (bufend != buf + 2) 165817721Speter break; 165917721Speter } 166017721Speter 166117721Speter if (i < 16 || *s != '\0') 166217721Speter error (1, 0, "Invalid Checksum response: `%s'", args); 166317721Speter 166417721Speter stored_checksum_valid = 1; 166517721Speter} 166617721Speter 166754427Speter/* Mode that we got in a "Mode" response (malloc'd), or NULL if none. */ 166817721Speterstatic char *stored_mode; 166917721Speter 167017721Speterstatic void handle_mode PROTO ((char *, int)); 167117721Speter 167217721Speterstatic void 167317721Speterhandle_mode (args, len) 167417721Speter char *args; 167517721Speter int len; 167617721Speter{ 167754427Speter if (stored_mode != NULL) 167817721Speter error (1, 0, "protocol error: duplicate Mode"); 167917721Speter stored_mode = xstrdup (args); 168017721Speter} 168126801Speter 168226801Speter/* Nonzero if time was specified in Mod-time. */ 168326801Speterstatic int stored_modtime_valid; 168426801Speter/* Time specified in Mod-time. */ 168526801Speterstatic time_t stored_modtime; 168617721Speter 168726801Speterstatic void handle_mod_time PROTO ((char *, int)); 168826801Speter 168926801Speterstatic void 169026801Speterhandle_mod_time (args, len) 169126801Speter char *args; 169226801Speter int len; 169326801Speter{ 169426801Speter if (stored_modtime_valid) 169526801Speter error (0, 0, "protocol error: duplicate Mod-time"); 169626801Speter stored_modtime = get_date (args, NULL); 169726801Speter if (stored_modtime == (time_t) -1) 169826801Speter error (0, 0, "protocol error: cannot parse date %s", args); 169926801Speter else 170026801Speter stored_modtime_valid = 1; 170126801Speter} 170226801Speter 170317721Speter/* 170417721Speter * If we receive a patch, but the patch program fails to apply it, we 170517721Speter * want to request the original file. We keep a list of files whose 170617721Speter * patches have failed. 170717721Speter */ 170817721Speter 170917721Speterchar **failed_patches; 171017721Speterint failed_patches_count; 171117721Speter 171217721Speterstruct update_entries_data 171317721Speter{ 171417721Speter enum { 171517721Speter /* 171617721Speter * We are just getting an Entries line; the local file is 171717721Speter * correct. 171817721Speter */ 171917721Speter UPDATE_ENTRIES_CHECKIN, 172017721Speter /* We are getting the file contents as well. */ 172117721Speter UPDATE_ENTRIES_UPDATE, 172217721Speter /* 172317721Speter * We are getting a patch against the existing local file, not 172417721Speter * an entire new file. 172517721Speter */ 172625839Speter UPDATE_ENTRIES_PATCH, 172725839Speter /* 172825839Speter * We are getting an RCS change text (diff -n output) against 172925839Speter * the existing local file, not an entire new file. 173025839Speter */ 173125839Speter UPDATE_ENTRIES_RCS_DIFF 173217721Speter } contents; 173317721Speter 173425839Speter enum { 173525839Speter /* We are replacing an existing file. */ 173625839Speter UPDATE_ENTRIES_EXISTING, 173725839Speter /* We are creating a new file. */ 173825839Speter UPDATE_ENTRIES_NEW, 173925839Speter /* We don't know whether it is existing or new. */ 174025839Speter UPDATE_ENTRIES_EXISTING_OR_NEW 174125839Speter } existp; 174225839Speter 174317721Speter /* 174417721Speter * String to put in the timestamp field or NULL to use the timestamp 174517721Speter * of the file. 174617721Speter */ 174717721Speter char *timestamp; 174817721Speter}; 174917721Speter 175017721Speter/* Update the Entries line for this file. */ 175117721Speterstatic void 175217721Speterupdate_entries (data_arg, ent_list, short_pathname, filename) 175317721Speter char *data_arg; 175417721Speter List *ent_list; 175517721Speter char *short_pathname; 175617721Speter char *filename; 175717721Speter{ 175817721Speter char *entries_line; 175917721Speter struct update_entries_data *data = (struct update_entries_data *)data_arg; 176017721Speter 176117721Speter char *cp; 176217721Speter char *user; 176317721Speter char *vn; 176417721Speter /* Timestamp field. Always empty according to the protocol. */ 176517721Speter char *ts; 176632785Speter char *options = NULL; 176725839Speter char *tag = NULL; 176825839Speter char *date = NULL; 176917721Speter char *tag_or_date; 177025839Speter char *scratch_entries = NULL; 177117721Speter int bin; 177217721Speter 177332785Speter#ifdef UTIME_EXPECTS_WRITABLE 177432785Speter int change_it_back = 0; 177532785Speter#endif 177632785Speter 177725839Speter read_line (&entries_line); 177817721Speter 177917721Speter /* 178017721Speter * Parse the entries line. 178117721Speter */ 178232785Speter scratch_entries = xstrdup (entries_line); 178317721Speter 178432785Speter if (scratch_entries[0] != '/') 178532785Speter error (1, 0, "bad entries line `%s' from server", entries_line); 178632785Speter user = scratch_entries + 1; 178732785Speter if ((cp = strchr (user, '/')) == NULL) 178832785Speter error (1, 0, "bad entries line `%s' from server", entries_line); 178932785Speter *cp++ = '\0'; 179032785Speter vn = cp; 179132785Speter if ((cp = strchr (vn, '/')) == NULL) 179232785Speter error (1, 0, "bad entries line `%s' from server", entries_line); 179332785Speter *cp++ = '\0'; 179432785Speter 179532785Speter ts = cp; 179632785Speter if ((cp = strchr (ts, '/')) == NULL) 179732785Speter error (1, 0, "bad entries line `%s' from server", entries_line); 179832785Speter *cp++ = '\0'; 179932785Speter options = cp; 180032785Speter if ((cp = strchr (options, '/')) == NULL) 180132785Speter error (1, 0, "bad entries line `%s' from server", entries_line); 180232785Speter *cp++ = '\0'; 180332785Speter tag_or_date = cp; 180432785Speter 180532785Speter /* If a slash ends the tag_or_date, ignore everything after it. */ 180632785Speter cp = strchr (tag_or_date, '/'); 180732785Speter if (cp != NULL) 180832785Speter *cp = '\0'; 180932785Speter if (*tag_or_date == 'T') 181032785Speter tag = tag_or_date + 1; 181132785Speter else if (*tag_or_date == 'D') 181232785Speter date = tag_or_date + 1; 181317721Speter 181432785Speter /* Done parsing the entries line. */ 181517721Speter 181617721Speter if (data->contents == UPDATE_ENTRIES_UPDATE 181725839Speter || data->contents == UPDATE_ENTRIES_PATCH 181825839Speter || data->contents == UPDATE_ENTRIES_RCS_DIFF) 181917721Speter { 182017721Speter char *size_string; 182117721Speter char *mode_string; 1822175266Sobrien size_t size; 182317721Speter char *buf; 182417721Speter char *temp_filename; 182525839Speter int use_gzip; 182625839Speter int patch_failed; 1827175266Sobrien char *s; 182817721Speter 182925839Speter read_line (&mode_string); 183017721Speter 183125839Speter read_line (&size_string); 183217721Speter if (size_string[0] == 'z') 183317721Speter { 183417721Speter use_gzip = 1; 1835175266Sobrien s = size_string + 1; 183617721Speter } 183717721Speter else 183817721Speter { 183917721Speter use_gzip = 0; 1840175266Sobrien s = size_string; 184117721Speter } 1842175266Sobrien size = strto_file_size (s); 184317721Speter free (size_string); 184417721Speter 184525839Speter /* Note that checking this separately from writing the file is 184654427Speter a race condition: if the existence or lack thereof of the 184754427Speter file changes between now and the actual calls which 184825839Speter operate on it, we lose. However (a) there are so many 184925839Speter cases, I'm reluctant to try to fix them all, (b) in some 185025839Speter cases the system might not even have a system call which 185125839Speter does the right thing, and (c) it isn't clear this needs to 185225839Speter work. */ 185325839Speter if (data->existp == UPDATE_ENTRIES_EXISTING 185425839Speter && !isfile (filename)) 185525839Speter /* Emit a warning and update the file anyway. */ 185625839Speter error (0, 0, "warning: %s unexpectedly disappeared", 185725839Speter short_pathname); 185825839Speter 185925839Speter if (data->existp == UPDATE_ENTRIES_NEW 186025839Speter && isfile (filename)) 186125839Speter { 186225839Speter /* Emit a warning and refuse to update the file; we don't want 186325839Speter to clobber a user's file. */ 186425839Speter size_t nread; 186525839Speter size_t toread; 186625839Speter 186725839Speter /* size should be unsigned, but until we get around to fixing 186825839Speter that, work around it. */ 186925839Speter size_t usize; 187025839Speter 187125839Speter char buf[8192]; 187225839Speter 187325839Speter /* This error might be confusing; it isn't really clear to 187425839Speter the user what to do about it. Keep in mind that it has 187525839Speter several causes: (1) something/someone creates the file 187625839Speter during the time that CVS is running, (2) the repository 187725839Speter has two files whose names clash for the client because 1878128269Speter of case-insensitivity or similar causes, See 3 for 1879128269Speter additional notes. (3) a special case of this is that a 1880128269Speter file gets renamed for example from a.c to A.C. A 1881128269Speter "cvs update" on a case-insensitive client will get this 1882128269Speter error. In this case and in case 2, the filename 1883128269Speter (short_pathname) printed in the error message will likely _not_ 1884128269Speter have the same case as seen by the user in a directory listing. 1885128269Speter (4) the client has a file which the server doesn't know 1886128269Speter about (e.g. "? foo" file), and that name clashes with a file 1887128269Speter the server does know about, (5) classify.c will print the same 188825839Speter message for other reasons. 188925839Speter 189025839Speter I hope the above paragraph makes it clear that making this 189125839Speter clearer is not a one-line fix. */ 189225839Speter error (0, 0, "move away %s; it is in the way", short_pathname); 189332785Speter if (updated_fname != NULL) 189432785Speter { 189532785Speter cvs_output ("C ", 0); 189632785Speter cvs_output (updated_fname, 0); 189732785Speter cvs_output ("\n", 1); 189832785Speter } 189932785Speter failure_exit = 1; 190025839Speter 190125839Speter discard_file_and_return: 190225839Speter /* Now read and discard the file contents. */ 190325839Speter usize = size; 190425839Speter nread = 0; 190525839Speter while (nread < usize) 190625839Speter { 190725839Speter toread = usize - nread; 190825839Speter if (toread > sizeof buf) 190925839Speter toread = sizeof buf; 191025839Speter 191125839Speter nread += try_read_from_server (buf, toread); 191225839Speter if (nread == usize) 191325839Speter break; 191425839Speter } 191525839Speter 191625839Speter free (mode_string); 191766528Speter free (scratch_entries); 191825839Speter free (entries_line); 191932785Speter 192032785Speter /* The Mode, Mod-time, and Checksum responses should not carry 192132785Speter over to a subsequent Created (or whatever) response, even 192232785Speter in the error case. */ 192332785Speter if (stored_mode != NULL) 192454427Speter { 192532785Speter free (stored_mode); 192654427Speter stored_mode = NULL; 192754427Speter } 192832785Speter stored_modtime_valid = 0; 192932785Speter stored_checksum_valid = 0; 193032785Speter 193132785Speter if (updated_fname != NULL) 193232785Speter { 193332785Speter free (updated_fname); 193432785Speter updated_fname = NULL; 193532785Speter } 193625839Speter return; 193725839Speter } 193825839Speter 193917721Speter temp_filename = xmalloc (strlen (filename) + 80); 194017721Speter#ifdef USE_VMS_FILENAMES 194117721Speter /* A VMS rename of "blah.dat" to "foo" to implies a 194217721Speter destination of "foo.dat" which is unfortinate for CVS */ 1943175266Sobrien sprintf (temp_filename, "%s_new_", filename); 194417721Speter#else 194517721Speter#ifdef _POSIX_NO_TRUNC 194617721Speter sprintf (temp_filename, ".new.%.9s", filename); 194717721Speter#else /* _POSIX_NO_TRUNC */ 194817721Speter sprintf (temp_filename, ".new.%s", filename); 194917721Speter#endif /* _POSIX_NO_TRUNC */ 195017721Speter#endif /* USE_VMS_FILENAMES */ 195125839Speter 195217721Speter buf = xmalloc (size); 195317721Speter 195417721Speter /* Some systems, like OS/2 and Windows NT, end lines with CRLF 195517721Speter instead of just LF. Format translation is done in the C 195617721Speter library I/O funtions. Here we tell them whether or not to 195717721Speter convert -- if this file is marked "binary" with the RCS -kb 195817721Speter flag, then we don't want to convert, else we do (because 195917721Speter CVS assumes text files by default). */ 196017721Speter 196117721Speter if (options) 196217721Speter bin = !(strcmp (options, "-kb")); 196317721Speter else 196417721Speter bin = 0; 196517721Speter 196625839Speter if (data->contents == UPDATE_ENTRIES_RCS_DIFF) 196725839Speter { 196825839Speter /* This is an RCS change text. We just hold the change 196925839Speter text in memory. */ 197017721Speter 197125839Speter if (use_gzip) 197225839Speter error (1, 0, 197325839Speter "server error: gzip invalid with RCS change text"); 197417721Speter 197525839Speter read_from_server (buf, size); 197625839Speter } 197725839Speter else 197817721Speter { 197925839Speter int fd; 198017721Speter 198125839Speter fd = CVS_OPEN (temp_filename, 198225839Speter (O_WRONLY | O_CREAT | O_TRUNC 198325839Speter | (bin ? OPEN_BINARY : 0)), 198425839Speter 0777); 198525839Speter 198625839Speter if (fd < 0) 198725839Speter { 198825839Speter /* I can see a case for making this a fatal error; for 198925839Speter a condition like disk full or network unreachable 199025839Speter (for a file server), carrying on and giving an 199125839Speter error on each file seems unnecessary. But if it is 199225839Speter a permission problem, or some such, then it is 199325839Speter entirely possible that future files will not have 199425839Speter the same problem. */ 199525839Speter error (0, errno, "cannot write %s", short_pathname); 1996175266Sobrien free (temp_filename); 1997175266Sobrien free (buf); 199825839Speter goto discard_file_and_return; 199925839Speter } 200025839Speter 200125839Speter if (size > 0) 200225839Speter { 200325839Speter read_from_server (buf, size); 200425839Speter 200532785Speter if (use_gzip) 200654427Speter { 200766528Speter if (gunzip_and_write (fd, short_pathname, 200866528Speter (unsigned char *) buf, size)) 200954427Speter error (1, 0, "aborting due to compression error"); 201054427Speter } 201132785Speter else if (write (fd, buf, size) != size) 201225839Speter error (1, errno, "writing %s", short_pathname); 201325839Speter } 201425839Speter 201525839Speter if (close (fd) < 0) 201625839Speter error (1, errno, "writing %s", short_pathname); 201732785Speter } 201825839Speter 201932785Speter /* This is after we have read the file from the net (a change 202032785Speter from previous versions, where the server would send us 202132785Speter "M U foo.c" before Update-existing or whatever), but before 202232785Speter we finish writing the file (arguably a bug). The timing 202332785Speter affects a user who wants status info about how far we have 202432785Speter gotten, and also affects whether "U foo.c" appears in addition 202532785Speter to various error messages. */ 202632785Speter if (updated_fname != NULL) 202732785Speter { 202832785Speter cvs_output ("U ", 0); 202932785Speter cvs_output (updated_fname, 0); 203032785Speter cvs_output ("\n", 1); 203132785Speter free (updated_fname); 203232785Speter updated_fname = 0; 203317721Speter } 203417721Speter 203525839Speter patch_failed = 0; 203625839Speter 203717721Speter if (data->contents == UPDATE_ENTRIES_UPDATE) 203817721Speter { 203917721Speter rename_file (temp_filename, filename); 204017721Speter } 204125839Speter else if (data->contents == UPDATE_ENTRIES_PATCH) 204217721Speter { 204354427Speter /* You might think we could just leave Patched out of 204454427Speter Valid-responses and not get this response. However, if 204554427Speter memory serves, the CVS 1.9 server bases this on -u 204654427Speter (update-patches), and there is no way for us to send -u 204754427Speter or not based on whether the server supports "Rcs-diff". 204854427Speter 204932785Speter Fall back to transmitting entire files. */ 205032785Speter patch_failed = 1; 205125839Speter } 205225839Speter else 205325839Speter { 205432785Speter char *filebuf; 205525839Speter size_t filebufsize; 205625839Speter size_t nread; 205725839Speter char *patchedbuf; 205825839Speter size_t patchedlen; 205917721Speter 206025839Speter /* Handle UPDATE_ENTRIES_RCS_DIFF. */ 206125839Speter 206225839Speter if (!isfile (filename)) 206325839Speter error (1, 0, "patch original file %s does not exist", 206425839Speter short_pathname); 206532785Speter filebuf = NULL; 206632785Speter filebufsize = 0; 206725839Speter nread = 0; 206825839Speter 206932785Speter get_file (filename, short_pathname, bin ? FOPEN_BINARY_READ : "r", 207032785Speter &filebuf, &filebufsize, &nread); 207125839Speter /* At this point the contents of the existing file are in 207225839Speter FILEBUF, and the length of the contents is in NREAD. 207325839Speter The contents of the patch from the network are in BUF, 207425839Speter and the length of the patch is in SIZE. */ 207525839Speter 207625839Speter if (! rcs_change_text (short_pathname, filebuf, nread, buf, size, 207725839Speter &patchedbuf, &patchedlen)) 207825839Speter patch_failed = 1; 207925839Speter else 208025839Speter { 208125839Speter if (stored_checksum_valid) 208225839Speter { 208354427Speter struct cvs_MD5Context context; 208425839Speter unsigned char checksum[16]; 208525839Speter 208625839Speter /* We have a checksum. Check it before writing 208725839Speter the file out, so that we don't have to read it 208825839Speter back in again. */ 208954427Speter cvs_MD5Init (&context); 209054427Speter cvs_MD5Update (&context, 209154427Speter (unsigned char *) patchedbuf, patchedlen); 209254427Speter cvs_MD5Final (checksum, &context); 209325839Speter if (memcmp (checksum, stored_checksum, 16) != 0) 209425839Speter { 209525839Speter error (0, 0, 209625839Speter "checksum failure after patch to %s; will refetch", 209725839Speter short_pathname); 209825839Speter 209925839Speter patch_failed = 1; 210025839Speter } 210125839Speter 210225839Speter stored_checksum_valid = 0; 210325839Speter } 210425839Speter 210525839Speter if (! patch_failed) 210625839Speter { 210732785Speter FILE *e; 210832785Speter 210925839Speter e = open_file (temp_filename, 211025839Speter bin ? FOPEN_BINARY_WRITE : "w"); 211125839Speter if (fwrite (patchedbuf, 1, patchedlen, e) != patchedlen) 211225839Speter error (1, errno, "cannot write %s", temp_filename); 211325839Speter if (fclose (e) == EOF) 211425839Speter error (1, errno, "cannot close %s", temp_filename); 211525839Speter rename_file (temp_filename, filename); 211625839Speter } 211725839Speter 211825839Speter free (patchedbuf); 211925839Speter } 212025839Speter 212125839Speter free (filebuf); 212217721Speter } 212325839Speter 212417721Speter free (temp_filename); 212517721Speter 212625839Speter if (stored_checksum_valid && ! patch_failed) 212717721Speter { 212817721Speter FILE *e; 212954427Speter struct cvs_MD5Context context; 213017721Speter unsigned char buf[8192]; 213117721Speter unsigned len; 213217721Speter unsigned char checksum[16]; 213317721Speter 213417721Speter /* 213517721Speter * Compute the MD5 checksum. This will normally only be 213617721Speter * used when receiving a patch, so we always compute it 213717721Speter * here on the final file, rather than on the received 213817721Speter * data. 213917721Speter * 214017721Speter * Note that if the file is a text file, we should read it 214117721Speter * here using text mode, so its lines will be terminated the same 214217721Speter * way they were transmitted. 214317721Speter */ 214425839Speter e = CVS_FOPEN (filename, "r"); 214517721Speter if (e == NULL) 214617721Speter error (1, errno, "could not open %s", short_pathname); 214717721Speter 214854427Speter cvs_MD5Init (&context); 214917721Speter while ((len = fread (buf, 1, sizeof buf, e)) != 0) 215054427Speter cvs_MD5Update (&context, buf, len); 215117721Speter if (ferror (e)) 215217721Speter error (1, errno, "could not read %s", short_pathname); 215354427Speter cvs_MD5Final (checksum, &context); 215417721Speter 215517721Speter fclose (e); 215617721Speter 215717721Speter stored_checksum_valid = 0; 215817721Speter 215917721Speter if (memcmp (checksum, stored_checksum, 16) != 0) 216017721Speter { 216117721Speter if (data->contents != UPDATE_ENTRIES_PATCH) 216217721Speter error (1, 0, "checksum failure on %s", 216317721Speter short_pathname); 216417721Speter 216517721Speter error (0, 0, 216617721Speter "checksum failure after patch to %s; will refetch", 216717721Speter short_pathname); 216817721Speter 216925839Speter patch_failed = 1; 217017721Speter } 217117721Speter } 217217721Speter 217325839Speter if (patch_failed) 217425839Speter { 217525839Speter /* Save this file to retrieve later. */ 217625839Speter failed_patches = (char **) xrealloc ((char *) failed_patches, 217725839Speter ((failed_patches_count + 1) 217825839Speter * sizeof (char *))); 217925839Speter failed_patches[failed_patches_count] = xstrdup (short_pathname); 218025839Speter ++failed_patches_count; 218125839Speter 218225839Speter stored_checksum_valid = 0; 218325839Speter 218425839Speter free (mode_string); 218525839Speter free (buf); 218666528Speter free (scratch_entries); 218766528Speter free (entries_line); 218825839Speter 218925839Speter return; 219025839Speter } 219125839Speter 219217721Speter { 219334461Speter int status = change_mode (filename, mode_string, 1); 219417721Speter if (status != 0) 219517721Speter error (0, status, "cannot change mode of %s", short_pathname); 219617721Speter } 219717721Speter 219817721Speter free (mode_string); 219917721Speter free (buf); 220017721Speter } 220117721Speter 220254427Speter if (stored_mode != NULL) 220354427Speter { 220434461Speter change_mode (filename, stored_mode, 1); 220554427Speter free (stored_mode); 220654427Speter stored_mode = NULL; 220754427Speter } 220854427Speter 220926801Speter if (stored_modtime_valid) 221026801Speter { 221126801Speter struct utimbuf t; 221226801Speter 221326801Speter memset (&t, 0, sizeof (t)); 2214128269Speter t.modtime = stored_modtime; 2215128269Speter (void) time (&t.actime); 221632785Speter 221732785Speter#ifdef UTIME_EXPECTS_WRITABLE 221832785Speter if (!iswritable (filename)) 221932785Speter { 222032785Speter xchmod (filename, 1); 222132785Speter change_it_back = 1; 222232785Speter } 222332785Speter#endif /* UTIME_EXPECTS_WRITABLE */ 222432785Speter 222526801Speter if (utime (filename, &t) < 0) 222626801Speter error (0, errno, "cannot set time on %s", filename); 222732785Speter 222832785Speter#ifdef UTIME_EXPECTS_WRITABLE 2229128269Speter if (change_it_back) 223032785Speter { 223132785Speter xchmod (filename, 0); 223232785Speter change_it_back = 0; 223332785Speter } 223432785Speter#endif /* UTIME_EXPECTS_WRITABLE */ 223532785Speter 223626801Speter stored_modtime_valid = 0; 223726801Speter } 223826801Speter 223917721Speter /* 224017721Speter * Process the entries line. Do this after we've written the file, 224117721Speter * since we need the timestamp. 224217721Speter */ 2243128269Speter if (strcmp (cvs_cmd_name, "export") != 0) 224417721Speter { 224517721Speter char *local_timestamp; 224617721Speter char *file_timestamp; 224717721Speter 224825839Speter (void) time (&last_register_time); 224925839Speter 225017721Speter local_timestamp = data->timestamp; 225117721Speter if (local_timestamp == NULL || ts[0] == '+') 225217721Speter file_timestamp = time_stamp (filename); 225317721Speter else 225417721Speter file_timestamp = NULL; 225517721Speter 225617721Speter /* 225717721Speter * These special version numbers signify that it is not up to 225817721Speter * date. Create a dummy timestamp which will never compare 225917721Speter * equal to the timestamp of the file. 226017721Speter */ 2261109660Speter if (vn[0] == '\0' || strcmp (vn, "0") == 0 || vn[0] == '-') 226217721Speter local_timestamp = "dummy timestamp"; 226317721Speter else if (local_timestamp == NULL) 226417721Speter { 226517721Speter local_timestamp = file_timestamp; 226654427Speter 2267128269Speter /* Checking for cvs_cmd_name of "commit" doesn't seem like 226854427Speter the cleanest way to handle this, but it seem to roughly 226954427Speter parallel what the :local: code which calls 227054427Speter mark_up_to_date ends up amounting to. Some day, should 227154427Speter think more about what the Checked-in response means 227254427Speter vis-a-vis both Entries and Base and clarify 227354427Speter cvsclient.texi accordingly. */ 227454427Speter 2275128269Speter if (!strcmp (cvs_cmd_name, "commit")) 227654427Speter mark_up_to_date (filename); 227717721Speter } 227817721Speter 227917721Speter Register (ent_list, filename, vn, local_timestamp, 228017721Speter options, tag, date, ts[0] == '+' ? file_timestamp : NULL); 228117721Speter 228217721Speter if (file_timestamp) 228317721Speter free (file_timestamp); 228417721Speter 228517721Speter } 228666528Speter free (scratch_entries); 228717721Speter free (entries_line); 228817721Speter} 228917721Speter 229017721Speterstatic void 229117721Speterhandle_checked_in (args, len) 229217721Speter char *args; 229317721Speter int len; 229417721Speter{ 229517721Speter struct update_entries_data dat; 229617721Speter dat.contents = UPDATE_ENTRIES_CHECKIN; 229725839Speter dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; 229817721Speter dat.timestamp = NULL; 229917721Speter call_in_directory (args, update_entries, (char *)&dat); 230017721Speter} 230117721Speter 230217721Speterstatic void 230317721Speterhandle_new_entry (args, len) 230417721Speter char *args; 230517721Speter int len; 230617721Speter{ 230717721Speter struct update_entries_data dat; 230817721Speter dat.contents = UPDATE_ENTRIES_CHECKIN; 230925839Speter dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; 231017721Speter dat.timestamp = "dummy timestamp from new-entry"; 231117721Speter call_in_directory (args, update_entries, (char *)&dat); 231217721Speter} 231317721Speter 231417721Speterstatic void 231517721Speterhandle_updated (args, len) 231617721Speter char *args; 231717721Speter int len; 231817721Speter{ 231917721Speter struct update_entries_data dat; 232017721Speter dat.contents = UPDATE_ENTRIES_UPDATE; 232125839Speter dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; 232217721Speter dat.timestamp = NULL; 232317721Speter call_in_directory (args, update_entries, (char *)&dat); 232417721Speter} 232517721Speter 232625839Speterstatic void handle_created PROTO((char *, int)); 232725839Speter 232817721Speterstatic void 232925839Speterhandle_created (args, len) 233025839Speter char *args; 233125839Speter int len; 233225839Speter{ 233325839Speter struct update_entries_data dat; 233425839Speter dat.contents = UPDATE_ENTRIES_UPDATE; 233525839Speter dat.existp = UPDATE_ENTRIES_NEW; 233625839Speter dat.timestamp = NULL; 233725839Speter call_in_directory (args, update_entries, (char *)&dat); 233825839Speter} 233925839Speter 234025839Speterstatic void handle_update_existing PROTO((char *, int)); 234125839Speter 234225839Speterstatic void 234325839Speterhandle_update_existing (args, len) 234425839Speter char *args; 234525839Speter int len; 234625839Speter{ 234725839Speter struct update_entries_data dat; 234825839Speter dat.contents = UPDATE_ENTRIES_UPDATE; 234925839Speter dat.existp = UPDATE_ENTRIES_EXISTING; 235025839Speter dat.timestamp = NULL; 235125839Speter call_in_directory (args, update_entries, (char *)&dat); 235225839Speter} 235325839Speter 235425839Speterstatic void 235517721Speterhandle_merged (args, len) 235617721Speter char *args; 235717721Speter int len; 235817721Speter{ 235917721Speter struct update_entries_data dat; 236017721Speter dat.contents = UPDATE_ENTRIES_UPDATE; 236125839Speter /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */ 236225839Speter dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; 236317721Speter dat.timestamp = "Result of merge"; 236417721Speter call_in_directory (args, update_entries, (char *)&dat); 236517721Speter} 236617721Speter 236717721Speterstatic void 236817721Speterhandle_patched (args, len) 236917721Speter char *args; 237017721Speter int len; 237117721Speter{ 237217721Speter struct update_entries_data dat; 237317721Speter dat.contents = UPDATE_ENTRIES_PATCH; 237425839Speter /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */ 237525839Speter dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; 237617721Speter dat.timestamp = NULL; 237717721Speter call_in_directory (args, update_entries, (char *)&dat); 237817721Speter} 237925839Speter 238025839Speterstatic void 238125839Speterhandle_rcs_diff (args, len) 238225839Speter char *args; 238325839Speter int len; 238425839Speter{ 238525839Speter struct update_entries_data dat; 238625839Speter dat.contents = UPDATE_ENTRIES_RCS_DIFF; 238725839Speter /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */ 238825839Speter dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW; 238925839Speter dat.timestamp = NULL; 239025839Speter call_in_directory (args, update_entries, (char *)&dat); 239125839Speter} 239217721Speter 239317721Speterstatic void 239417721Speterremove_entry (data, ent_list, short_pathname, filename) 239517721Speter char *data; 239617721Speter List *ent_list; 239717721Speter char *short_pathname; 239817721Speter char *filename; 239917721Speter{ 240017721Speter Scratch_Entry (ent_list, filename); 240117721Speter} 240217721Speter 240317721Speterstatic void 240417721Speterhandle_remove_entry (args, len) 240517721Speter char *args; 240617721Speter int len; 240717721Speter{ 240817721Speter call_in_directory (args, remove_entry, (char *)NULL); 240917721Speter} 241017721Speter 241117721Speterstatic void 241217721Speterremove_entry_and_file (data, ent_list, short_pathname, filename) 241317721Speter char *data; 241417721Speter List *ent_list; 241517721Speter char *short_pathname; 241617721Speter char *filename; 241717721Speter{ 241817721Speter Scratch_Entry (ent_list, filename); 241925839Speter /* Note that we don't ignore existence_error's here. The server 242025839Speter should be sending Remove-entry rather than Removed in cases 242125839Speter where the file does not exist. And if the user removes the 242225839Speter file halfway through a cvs command, we should be printing an 242325839Speter error. */ 242417721Speter if (unlink_file (filename) < 0) 242517721Speter error (0, errno, "unable to remove %s", short_pathname); 242617721Speter} 242717721Speter 242817721Speterstatic void 242917721Speterhandle_removed (args, len) 243017721Speter char *args; 243117721Speter int len; 243217721Speter{ 243317721Speter call_in_directory (args, remove_entry_and_file, (char *)NULL); 243417721Speter} 243517721Speter 243617721Speter/* Is this the top level (directory containing CVSROOT)? */ 243717721Speterstatic int 243817721Speteris_cvsroot_level (pathname) 243917721Speter char *pathname; 244017721Speter{ 244181407Speter if (strcmp (toplevel_repos, current_parsed_root->directory) != 0) 244217721Speter return 0; 244317721Speter 244425839Speter return strchr (pathname, '/') == NULL; 244517721Speter} 244617721Speter 244717721Speterstatic void 244817721Speterset_static (data, ent_list, short_pathname, filename) 244917721Speter char *data; 245017721Speter List *ent_list; 245117721Speter char *short_pathname; 245217721Speter char *filename; 245317721Speter{ 245417721Speter FILE *fp; 245517721Speter fp = open_file (CVSADM_ENTSTAT, "w+"); 245617721Speter if (fclose (fp) == EOF) 245717721Speter error (1, errno, "cannot close %s", CVSADM_ENTSTAT); 245817721Speter} 245917721Speter 246017721Speterstatic void 246117721Speterhandle_set_static_directory (args, len) 246217721Speter char *args; 246317721Speter int len; 246417721Speter{ 2465128269Speter if (strcmp (cvs_cmd_name, "export") == 0) 246617721Speter { 246717721Speter /* Swallow the repository. */ 246825839Speter read_line (NULL); 246917721Speter return; 247017721Speter } 247117721Speter call_in_directory (args, set_static, (char *)NULL); 247217721Speter} 247317721Speter 247417721Speterstatic void 247517721Speterclear_static (data, ent_list, short_pathname, filename) 247617721Speter char *data; 247717721Speter List *ent_list; 247817721Speter char *short_pathname; 247917721Speter char *filename; 248017721Speter{ 248117721Speter if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno)) 248217721Speter error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT); 248317721Speter} 248417721Speter 248517721Speterstatic void 248617721Speterhandle_clear_static_directory (pathname, len) 248717721Speter char *pathname; 248817721Speter int len; 248917721Speter{ 2490128269Speter if (strcmp (cvs_cmd_name, "export") == 0) 249117721Speter { 249217721Speter /* Swallow the repository. */ 249325839Speter read_line (NULL); 249417721Speter return; 249517721Speter } 249617721Speter 249717721Speter if (is_cvsroot_level (pathname)) 249817721Speter { 249917721Speter /* 250017721Speter * Top level (directory containing CVSROOT). This seems to normally 250117721Speter * lack a CVS directory, so don't try to create files in it. 250217721Speter */ 250317721Speter return; 250417721Speter } 250517721Speter call_in_directory (pathname, clear_static, (char *)NULL); 250617721Speter} 250717721Speter 250817721Speterstatic void 250917721Speterset_sticky (data, ent_list, short_pathname, filename) 251017721Speter char *data; 251117721Speter List *ent_list; 251217721Speter char *short_pathname; 251317721Speter char *filename; 251417721Speter{ 251517721Speter char *tagspec; 251617721Speter FILE *f; 251717721Speter 251825839Speter read_line (&tagspec); 251954427Speter 252054427Speter /* FIXME-update-dir: error messages should include the directory. */ 252154427Speter f = CVS_FOPEN (CVSADM_TAG, "w+"); 252254427Speter if (f == NULL) 252354427Speter { 252454427Speter /* Making this non-fatal is a bit of a kludge (see dirs2 252554427Speter in testsuite). A better solution would be to avoid having 252654427Speter the server tell us about a directory we shouldn't be doing 252754427Speter anything with anyway (e.g. by handling directory 252854427Speter addition/removal better). */ 252954427Speter error (0, errno, "cannot open %s", CVSADM_TAG); 253054427Speter free (tagspec); 253154427Speter return; 253254427Speter } 253317721Speter if (fprintf (f, "%s\n", tagspec) < 0) 253417721Speter error (1, errno, "writing %s", CVSADM_TAG); 253517721Speter if (fclose (f) == EOF) 253617721Speter error (1, errno, "closing %s", CVSADM_TAG); 253717721Speter free (tagspec); 253817721Speter} 253917721Speter 254017721Speterstatic void 254117721Speterhandle_set_sticky (pathname, len) 254217721Speter char *pathname; 254317721Speter int len; 254417721Speter{ 2545128269Speter if (strcmp (cvs_cmd_name, "export") == 0) 254617721Speter { 254717721Speter /* Swallow the repository. */ 254825839Speter read_line (NULL); 254917721Speter /* Swallow the tag line. */ 255025839Speter read_line (NULL); 255117721Speter return; 255217721Speter } 255317721Speter if (is_cvsroot_level (pathname)) 255417721Speter { 255517721Speter /* 255617721Speter * Top level (directory containing CVSROOT). This seems to normally 255717721Speter * lack a CVS directory, so don't try to create files in it. 255817721Speter */ 255917721Speter 256017721Speter /* Swallow the repository. */ 256125839Speter read_line (NULL); 256217721Speter /* Swallow the tag line. */ 256325839Speter read_line (NULL); 256417721Speter return; 256517721Speter } 256617721Speter 256717721Speter call_in_directory (pathname, set_sticky, (char *)NULL); 256817721Speter} 256917721Speter 257017721Speterstatic void 257117721Speterclear_sticky (data, ent_list, short_pathname, filename) 257217721Speter char *data; 257317721Speter List *ent_list; 257417721Speter char *short_pathname; 257517721Speter char *filename; 257617721Speter{ 257717721Speter if (unlink_file (CVSADM_TAG) < 0 && ! existence_error (errno)) 257817721Speter error (1, errno, "cannot remove %s", CVSADM_TAG); 257917721Speter} 258017721Speter 258117721Speterstatic void 258217721Speterhandle_clear_sticky (pathname, len) 258317721Speter char *pathname; 258417721Speter int len; 258517721Speter{ 2586128269Speter if (strcmp (cvs_cmd_name, "export") == 0) 258717721Speter { 258817721Speter /* Swallow the repository. */ 258925839Speter read_line (NULL); 259017721Speter return; 259117721Speter } 259217721Speter 259317721Speter if (is_cvsroot_level (pathname)) 259417721Speter { 259517721Speter /* 259617721Speter * Top level (directory containing CVSROOT). This seems to normally 259717721Speter * lack a CVS directory, so don't try to create files in it. 259817721Speter */ 259917721Speter return; 260017721Speter } 260117721Speter 260217721Speter call_in_directory (pathname, clear_sticky, (char *)NULL); 260317721Speter} 260417721Speter 260517721Speter 260617721Speterstatic void template PROTO ((char *, List *, char *, char *)); 260717721Speter 260817721Speterstatic void 260917721Spetertemplate (data, ent_list, short_pathname, filename) 261017721Speter char *data; 261117721Speter List *ent_list; 261217721Speter char *short_pathname; 261317721Speter char *filename; 261417721Speter{ 2615128269Speter char *buf = xmalloc ( strlen ( short_pathname ) 2616128269Speter + strlen ( CVSADM_TEMPLATE ) 2617128269Speter + 2 ); 2618128269Speter sprintf ( buf, "%s/%s", short_pathname, CVSADM_TEMPLATE ); 2619128269Speter read_counted_file ( CVSADM_TEMPLATE, buf ); 2620128269Speter free ( buf ); 262117721Speter} 262217721Speter 262317721Speterstatic void handle_template PROTO ((char *, int)); 262417721Speter 262517721Speterstatic void 262617721Speterhandle_template (pathname, len) 262717721Speter char *pathname; 262817721Speter int len; 262917721Speter{ 263017721Speter call_in_directory (pathname, template, NULL); 263117721Speter} 263217721Speter 263317721Speter 263417721Speter 263517721Speterstruct save_dir { 263617721Speter char *dir; 263717721Speter struct save_dir *next; 263817721Speter}; 263917721Speter 264017721Speterstruct save_dir *prune_candidates; 264117721Speter 264217721Speterstatic void 264317721Speteradd_prune_candidate (dir) 2644128269Speter const char *dir; 264517721Speter{ 264617721Speter struct save_dir *p; 264717721Speter 264825839Speter if ((dir[0] == '.' && dir[1] == '\0') 264925839Speter || (prune_candidates != NULL 265025839Speter && strcmp (dir, prune_candidates->dir) == 0)) 265117721Speter return; 265217721Speter p = (struct save_dir *) xmalloc (sizeof (struct save_dir)); 265317721Speter p->dir = xstrdup (dir); 265417721Speter p->next = prune_candidates; 265517721Speter prune_candidates = p; 265617721Speter} 265717721Speter 265817721Speterstatic void process_prune_candidates PROTO((void)); 265917721Speter 266017721Speterstatic void 266117721Speterprocess_prune_candidates () 266217721Speter{ 266317721Speter struct save_dir *p; 266417721Speter struct save_dir *q; 266517721Speter 266625839Speter if (toplevel_wd != NULL) 266725839Speter { 266825839Speter if (CVS_CHDIR (toplevel_wd) < 0) 266925839Speter error (1, errno, "could not chdir to %s", toplevel_wd); 267025839Speter } 267117721Speter for (p = prune_candidates; p != NULL; ) 267217721Speter { 267325839Speter if (isemptydir (p->dir, 1)) 267417721Speter { 267525839Speter char *b; 267625839Speter 267725839Speter if (unlink_file_dir (p->dir) < 0) 267825839Speter error (0, errno, "cannot remove %s", p->dir); 267925839Speter b = strrchr (p->dir, '/'); 268025839Speter if (b == NULL) 268125839Speter Subdir_Deregister ((List *) NULL, (char *) NULL, p->dir); 268225839Speter else 268325839Speter { 268425839Speter *b = '\0'; 268525839Speter Subdir_Deregister ((List *) NULL, p->dir, b + 1); 268625839Speter } 268717721Speter } 268817721Speter free (p->dir); 268917721Speter q = p->next; 269017721Speter free (p); 269117721Speter p = q; 269217721Speter } 269325839Speter prune_candidates = NULL; 269417721Speter} 269517721Speter 269617721Speter/* Send a Repository line. */ 269717721Speter 269817721Speterstatic char *last_repos; 269917721Speterstatic char *last_update_dir; 270017721Speter 2701128269Speterstatic void send_repository PROTO((const char *, const char *, const char *)); 270217721Speter 270317721Speterstatic void 270417721Spetersend_repository (dir, repos, update_dir) 2705128269Speter const char *dir; 2706128269Speter const char *repos; 2707128269Speter const char *update_dir; 270817721Speter{ 270917721Speter char *adm_name; 271017721Speter 271117721Speter /* FIXME: this is probably not the best place to check; I wish I 271217721Speter * knew where in here's callers to really trap this bug. To 271317721Speter * reproduce the bug, just do this: 271417721Speter * 271517721Speter * mkdir junk 271617721Speter * cd junk 271717721Speter * cvs -d some_repos update foo 271817721Speter * 271917721Speter * Poof, CVS seg faults and dies! It's because it's trying to 272017721Speter * send a NULL string to the server but dies in send_to_server. 272117721Speter * That string was supposed to be the repository, but it doesn't 272217721Speter * get set because there's no CVSADM dir, and somehow it's not 272317721Speter * getting set from the -d argument either... ? 272417721Speter */ 272517721Speter if (repos == NULL) 272617721Speter { 272717721Speter /* Lame error. I want a real fix but can't stay up to track 272817721Speter this down right now. */ 272917721Speter error (1, 0, "no repository"); 273017721Speter } 273117721Speter 273217721Speter if (update_dir == NULL || update_dir[0] == '\0') 273317721Speter update_dir = "."; 273417721Speter 273517721Speter if (last_repos != NULL 273617721Speter && strcmp (repos, last_repos) == 0 273717721Speter && last_update_dir != NULL 273817721Speter && strcmp (update_dir, last_update_dir) == 0) 273917721Speter /* We've already sent it. */ 274017721Speter return; 274117721Speter 274217721Speter if (client_prune_dirs) 274317721Speter add_prune_candidate (update_dir); 274417721Speter 274554427Speter /* Add a directory name to the list of those sent to the 274654427Speter server. */ 274754427Speter if (update_dir && (*update_dir != '\0') 274854427Speter && (strcmp (update_dir, ".") != 0) 274954427Speter && (findnode (dirs_sent_to_server, update_dir) == NULL)) 275054427Speter { 275154427Speter Node *n; 275254427Speter n = getnode (); 275366528Speter n->type = NT_UNKNOWN; 275454427Speter n->key = xstrdup (update_dir); 275554427Speter n->data = NULL; 275654427Speter 275754427Speter if (addnode (dirs_sent_to_server, n)) 275854427Speter error (1, 0, "cannot add directory %s to list", n->key); 275954427Speter } 276054427Speter 276117721Speter /* 80 is large enough for any of CVSADM_*. */ 276217721Speter adm_name = xmalloc (strlen (dir) + 80); 276317721Speter 276425839Speter send_to_server ("Directory ", 0); 276517721Speter { 276625839Speter /* Send the directory name. I know that this 276725839Speter sort of duplicates code elsewhere, but each 276825839Speter case seems slightly different... */ 276925839Speter char buf[1]; 2770128269Speter const char *p = update_dir; 277125839Speter while (*p != '\0') 277225839Speter { 277325839Speter assert (*p != '\012'); 277425839Speter if (ISDIRSEP (*p)) 277525839Speter { 277625839Speter buf[0] = '/'; 277725839Speter send_to_server (buf, 1); 277825839Speter } 277925839Speter else 278025839Speter { 278125839Speter buf[0] = *p; 278225839Speter send_to_server (buf, 1); 278325839Speter } 278425839Speter ++p; 278525839Speter } 278617721Speter } 278725839Speter send_to_server ("\012", 1); 278825839Speter send_to_server (repos, 0); 278925839Speter send_to_server ("\012", 1); 279025839Speter 2791177394Sobrien if (strcmp (cvs_cmd_name, "import") 2792177394Sobrien && supported_request ("Static-directory")) 279317721Speter { 279417721Speter adm_name[0] = '\0'; 279517721Speter if (dir[0] != '\0') 279617721Speter { 279717721Speter strcat (adm_name, dir); 279817721Speter strcat (adm_name, "/"); 279917721Speter } 280017721Speter strcat (adm_name, CVSADM_ENTSTAT); 280117721Speter if (isreadable (adm_name)) 280217721Speter { 280317721Speter send_to_server ("Static-directory\012", 0); 280417721Speter } 280517721Speter } 2806177394Sobrien if (strcmp (cvs_cmd_name, "import") 2807177394Sobrien && supported_request ("Sticky")) 280817721Speter { 280917721Speter FILE *f; 281017721Speter if (dir[0] == '\0') 281117721Speter strcpy (adm_name, CVSADM_TAG); 281217721Speter else 281317721Speter sprintf (adm_name, "%s/%s", dir, CVSADM_TAG); 281417721Speter 281525839Speter f = CVS_FOPEN (adm_name, "r"); 281617721Speter if (f == NULL) 281717721Speter { 281817721Speter if (! existence_error (errno)) 281917721Speter error (1, errno, "reading %s", adm_name); 282017721Speter } 282117721Speter else 282217721Speter { 282317721Speter char line[80]; 282425839Speter char *nl = NULL; 282517721Speter send_to_server ("Sticky ", 0); 282617721Speter while (fgets (line, sizeof (line), f) != NULL) 282717721Speter { 282817721Speter send_to_server (line, 0); 282917721Speter nl = strchr (line, '\n'); 283017721Speter if (nl != NULL) 283117721Speter break; 283217721Speter } 283317721Speter if (nl == NULL) 283417721Speter send_to_server ("\012", 1); 283517721Speter if (fclose (f) == EOF) 283617721Speter error (0, errno, "closing %s", adm_name); 283717721Speter } 283817721Speter } 283917721Speter free (adm_name); 284017721Speter if (last_repos != NULL) 284117721Speter free (last_repos); 284217721Speter if (last_update_dir != NULL) 284317721Speter free (last_update_dir); 284417721Speter last_repos = xstrdup (repos); 284517721Speter last_update_dir = xstrdup (update_dir); 284617721Speter} 284717721Speter 284817721Speter/* Send a Repository line and set toplevel_repos. */ 284917721Speter 285032785Spetervoid 2851128269Spetersend_a_repository (dir, repository, update_dir_in) 2852128269Speter const char *dir; 2853128269Speter const char *repository; 2854128269Speter const char *update_dir_in; 285517721Speter{ 2856175266Sobrien char *update_dir; 2857128269Speter 2858175266Sobrien assert (update_dir_in); 2859175266Sobrien update_dir = xstrdup (update_dir_in); 2860175266Sobrien 286117721Speter if (toplevel_repos == NULL && repository != NULL) 286217721Speter { 286317721Speter if (update_dir[0] == '\0' 286417721Speter || (update_dir[0] == '.' && update_dir[1] == '\0')) 286517721Speter toplevel_repos = xstrdup (repository); 286617721Speter else 286717721Speter { 286817721Speter /* 286917721Speter * Get the repository from a CVS/Repository file if update_dir 287017721Speter * is absolute. This is not correct in general, because 287117721Speter * the CVS/Repository file might not be the top-level one. 287217721Speter * This is for cases like "cvs update /foo/bar" (I'm not 287317721Speter * sure it matters what toplevel_repos we get, but it does 287417721Speter * matter that we don't hit the "internal error" code below). 287517721Speter */ 287617721Speter if (update_dir[0] == '/') 287717721Speter toplevel_repos = Name_Repository (update_dir, update_dir); 287817721Speter else 287917721Speter { 288017721Speter /* 288117721Speter * Guess the repository of that directory by looking at a 288217721Speter * subdirectory and removing as many pathname components 288317721Speter * as are in update_dir. I think that will always (or at 288417721Speter * least almost always) be 1. 288517721Speter * 288617721Speter * So this deals with directories which have been 288717721Speter * renamed, though it doesn't necessarily deal with 288817721Speter * directories which have been put inside other 288917721Speter * directories (and cvs invoked on the containing 289017721Speter * directory). I'm not sure the latter case needs to 289117721Speter * work. 289254427Speter * 289354427Speter * 21 Aug 1998: Well, Mr. Above-Comment-Writer, it 289454427Speter * does need to work after all. When we are using the 289554427Speter * client in a multi-cvsroot environment, it will be 289654427Speter * fairly common that we have the above case (e.g., 289754427Speter * cwd checked out from one repository but 289854427Speter * subdirectory checked out from another). We can't 289954427Speter * assume that by walking up a directory in our wd we 290054427Speter * necessarily walk up a directory in the repository. 290117721Speter */ 290217721Speter /* 290317721Speter * This gets toplevel_repos wrong for "cvs update ../foo" 290417721Speter * but I'm not sure toplevel_repos matters in that case. 290517721Speter */ 290617721Speter 290754427Speter int repository_len, update_dir_len; 290854427Speter 290932785Speter strip_trailing_slashes (update_dir); 291032785Speter 291154427Speter repository_len = strlen (repository); 291254427Speter update_dir_len = strlen (update_dir); 291317721Speter 291454427Speter /* Try to remove the path components in UPDATE_DIR 291554427Speter from REPOSITORY. If the path elements don't exist 291654427Speter in REPOSITORY, or the removal of those path 291754427Speter elements mean that we "step above" 291881407Speter current_parsed_root->directory, set toplevel_repos to 291981407Speter current_parsed_root->directory. */ 292054427Speter if ((repository_len > update_dir_len) 292154427Speter && (strcmp (repository + repository_len - update_dir_len, 292254427Speter update_dir) == 0) 292381407Speter /* TOPLEVEL_REPOS shouldn't be above current_parsed_root->directory */ 2924109660Speter && ((size_t)(repository_len - update_dir_len) 292581407Speter > strlen (current_parsed_root->directory))) 292617721Speter { 292754427Speter /* The repository name contains UPDATE_DIR. Set 292854427Speter toplevel_repos to the repository name without 292954427Speter UPDATE_DIR. */ 293054427Speter 293154427Speter toplevel_repos = xmalloc (repository_len - update_dir_len); 293254427Speter /* Note that we don't copy the trailing '/'. */ 293354427Speter strncpy (toplevel_repos, repository, 293454427Speter repository_len - update_dir_len - 1); 293554427Speter toplevel_repos[repository_len - update_dir_len - 1] = '\0'; 293617721Speter } 293754427Speter else 293854427Speter { 293981407Speter toplevel_repos = xstrdup (current_parsed_root->directory); 294054427Speter } 294117721Speter } 294217721Speter } 294317721Speter } 294417721Speter 294517721Speter send_repository (dir, repository, update_dir); 2946128269Speter free (update_dir); 294717721Speter} 2948128269Speter 2949128269Speter 2950128269Speter 295117721Speter/* The "expanded" modules. */ 295217721Speterstatic int modules_count; 295317721Speterstatic int modules_allocated; 295417721Speterstatic char **modules_vector; 295517721Speter 295617721Speterstatic void 295717721Speterhandle_module_expansion (args, len) 295817721Speter char *args; 295917721Speter int len; 296017721Speter{ 296117721Speter if (modules_vector == NULL) 296217721Speter { 296317721Speter modules_allocated = 1; /* Small for testing */ 296417721Speter modules_vector = (char **) xmalloc 296517721Speter (modules_allocated * sizeof (modules_vector[0])); 296617721Speter } 296717721Speter else if (modules_count >= modules_allocated) 296817721Speter { 296917721Speter modules_allocated *= 2; 297017721Speter modules_vector = (char **) xrealloc 297117721Speter ((char *) modules_vector, 297217721Speter modules_allocated * sizeof (modules_vector[0])); 297317721Speter } 297417721Speter modules_vector[modules_count] = xmalloc (strlen (args) + 1); 297517721Speter strcpy (modules_vector[modules_count], args); 297617721Speter ++modules_count; 297717721Speter} 297817721Speter 297917721Speter/* Original, not "expanded" modules. */ 298017721Speterstatic int module_argc; 298117721Speterstatic char **module_argv; 298217721Speter 298317721Spetervoid 298417721Speterclient_expand_modules (argc, argv, local) 298517721Speter int argc; 298617721Speter char **argv; 298717721Speter int local; 298817721Speter{ 298917721Speter int errs; 299017721Speter int i; 299117721Speter 299217721Speter module_argc = argc; 299317721Speter module_argv = (char **) xmalloc ((argc + 1) * sizeof (module_argv[0])); 299417721Speter for (i = 0; i < argc; ++i) 299517721Speter module_argv[i] = xstrdup (argv[i]); 299617721Speter module_argv[argc] = NULL; 299717721Speter 299817721Speter for (i = 0; i < argc; ++i) 299917721Speter send_arg (argv[i]); 300081407Speter send_a_repository ("", current_parsed_root->directory, ""); 300117721Speter 300217721Speter send_to_server ("expand-modules\012", 0); 300317721Speter 300417721Speter errs = get_server_responses (); 300517721Speter if (last_repos != NULL) 300617721Speter free (last_repos); 300717721Speter last_repos = NULL; 300817721Speter if (last_update_dir != NULL) 300917721Speter free (last_update_dir); 301017721Speter last_update_dir = NULL; 301117721Speter if (errs) 301217721Speter error (errs, 0, "cannot expand modules"); 301317721Speter} 301417721Speter 301517721Spetervoid 301625839Speterclient_send_expansions (local, where, build_dirs) 301725839Speter int local; 301825839Speter char *where; 301925839Speter int build_dirs; 302017721Speter{ 302117721Speter int i; 302217721Speter char *argv[1]; 302317721Speter 302417721Speter /* Send the original module names. The "expanded" module name might 302517721Speter not be suitable as an argument to a co request (e.g. it might be 302617721Speter the result of a -d argument in the modules file). It might be 302717721Speter cleaner if we genuinely expanded module names, all the way to a 302817721Speter local directory and repository, but that isn't the way it works 302917721Speter now. */ 303017721Speter send_file_names (module_argc, module_argv, 0); 303117721Speter 303217721Speter for (i = 0; i < modules_count; ++i) 303317721Speter { 303425839Speter argv[0] = where ? where : modules_vector[i]; 303517721Speter if (isfile (argv[0])) 303625839Speter send_files (1, argv, local, 0, build_dirs ? SEND_BUILD_DIRS : 0); 303717721Speter } 303881407Speter send_a_repository ("", current_parsed_root->directory, ""); 303917721Speter} 304017721Speter 304117721Spetervoid 304217721Speterclient_nonexpanded_setup () 304317721Speter{ 304481407Speter send_a_repository ("", current_parsed_root->directory, ""); 304517721Speter} 304617721Speter 304732896Speter/* Receive a cvswrappers line from the server; it must be a line 304832896Speter containing an RCS option (e.g., "*.exe -k 'b'"). 304932896Speter 305032896Speter Note that this doesn't try to handle -t/-f options (which are a 305132896Speter whole separate issue which noone has thought much about, as far 305232896Speter as I know). 305332896Speter 305432896Speter We need to know the keyword expansion mode so we know whether to 305532896Speter read the file in text or binary mode. */ 305632896Speter 305717721Speterstatic void 305832896Speterhandle_wrapper_rcs_option (args, len) 305932896Speter char *args; 306032896Speter int len; 306132896Speter{ 306232896Speter char *p; 306332896Speter 306432896Speter /* Enforce the notes in cvsclient.texi about how the response is not 306532896Speter as free-form as it looks. */ 306632896Speter p = strchr (args, ' '); 306732896Speter if (p == NULL) 306844852Speter goto handle_error; 306932896Speter if (*++p != '-' 307032896Speter || *++p != 'k' 307132896Speter || *++p != ' ' 307232896Speter || *++p != '\'') 307344852Speter goto handle_error; 307432896Speter if (strchr (p, '\'') == NULL) 307544852Speter goto handle_error; 307632896Speter 307732896Speter /* Add server-side cvswrappers line to our wrapper list. */ 307832896Speter wrap_add (args, 0); 307932896Speter return; 308044852Speter handle_error: 308132896Speter error (0, errno, "protocol error: ignoring invalid wrappers %s", args); 308232896Speter} 308332896Speter 308432896Speter 308532896Speterstatic void 308617721Speterhandle_m (args, len) 308717721Speter char *args; 308817721Speter int len; 308917721Speter{ 309025839Speter /* In the case where stdout and stderr point to the same place, 309125839Speter fflushing stderr will make output happen in the correct order. 309225839Speter Often stderr will be line-buffered and this won't be needed, 309332785Speter but not always (is that true? I think the comment is probably 309432785Speter based on being confused between default buffering between 309532785Speter stdout and stderr. But I'm not sure). */ 309625839Speter fflush (stderr); 309725839Speter fwrite (args, len, sizeof (*args), stdout); 309825839Speter putc ('\n', stdout); 309917721Speter} 310017721Speter 310132785Speterstatic void handle_mbinary PROTO ((char *, int)); 310232785Speter 310317721Speterstatic void 310432785Speterhandle_mbinary (args, len) 310532785Speter char *args; 310632785Speter int len; 310732785Speter{ 310832785Speter char *size_string; 310932785Speter size_t size; 311032785Speter size_t totalread; 311132785Speter size_t nread; 311232785Speter size_t toread; 311332785Speter char buf[8192]; 311432785Speter 311532785Speter /* See comment at handle_m about (non)flush of stderr. */ 311632785Speter 311732785Speter /* Get the size. */ 311832785Speter read_line (&size_string); 3119175266Sobrien size = strto_file_size (size_string); 312032785Speter free (size_string); 312132785Speter 312232785Speter /* OK, now get all the data. The algorithm here is that we read 312332785Speter as much as the network wants to give us in 312432785Speter try_read_from_server, and then we output it all, and then 312532785Speter repeat, until we get all the data. */ 312632785Speter totalread = 0; 312732785Speter while (totalread < size) 312832785Speter { 312932785Speter toread = size - totalread; 313032785Speter if (toread > sizeof buf) 313132785Speter toread = sizeof buf; 313232785Speter 313332785Speter nread = try_read_from_server (buf, toread); 313432785Speter cvs_output_binary (buf, nread); 313532785Speter totalread += nread; 313632785Speter } 313732785Speter} 313832785Speter 313932785Speterstatic void 314017721Speterhandle_e (args, len) 314117721Speter char *args; 314217721Speter int len; 314317721Speter{ 314425839Speter /* In the case where stdout and stderr point to the same place, 314525839Speter fflushing stdout will make output happen in the correct order. */ 314625839Speter fflush (stdout); 314725839Speter fwrite (args, len, sizeof (*args), stderr); 314825839Speter putc ('\n', stderr); 314917721Speter} 315017721Speter 315125839Speter/*ARGSUSED*/ 315225839Speterstatic void 315325839Speterhandle_f (args, len) 315425839Speter char *args; 315525839Speter int len; 315625839Speter{ 315725839Speter fflush (stderr); 315825839Speter} 315925839Speter 316032785Speterstatic void handle_mt PROTO ((char *, int)); 316132785Speter 316232785Speterstatic void 316332785Speterhandle_mt (args, len) 316432785Speter char *args; 316532785Speter int len; 316632785Speter{ 316732785Speter char *p; 316832785Speter char *tag = args; 316932785Speter char *text; 317032785Speter 317132785Speter /* See comment at handle_m for more details. */ 317232785Speter fflush (stderr); 317332785Speter 317432785Speter p = strchr (args, ' '); 317532785Speter if (p == NULL) 317632785Speter text = NULL; 317732785Speter else 317832785Speter { 317932785Speter *p++ = '\0'; 318032785Speter text = p; 318132785Speter } 318232785Speter 318332785Speter switch (tag[0]) 318432785Speter { 318532785Speter case '+': 318632785Speter if (strcmp (tag, "+updated") == 0) 318732785Speter updated_seen = 1; 318854427Speter else if (strcmp (tag, "+importmergecmd") == 0) 318954427Speter importmergecmd.seen = 1; 319032785Speter break; 319132785Speter case '-': 319232785Speter if (strcmp (tag, "-updated") == 0) 319332785Speter updated_seen = 0; 319454427Speter else if (strcmp (tag, "-importmergecmd") == 0) 319554427Speter { 319654427Speter char buf[80]; 319754427Speter 319854427Speter /* Now that we have gathered the information, we can 319954427Speter output the suggested merge command. */ 320054427Speter 320154427Speter if (importmergecmd.conflicts == 0 320254427Speter || importmergecmd.mergetag1 == NULL 320354427Speter || importmergecmd.mergetag2 == NULL 320454427Speter || importmergecmd.repository == NULL) 320554427Speter { 320654427Speter error (0, 0, 320754427Speter "invalid server: incomplete importmergecmd tags"); 320854427Speter break; 320954427Speter } 321054427Speter 321154427Speter sprintf (buf, "\n%d conflicts created by this import.\n", 321254427Speter importmergecmd.conflicts); 321354427Speter cvs_output (buf, 0); 321454427Speter cvs_output ("Use the following command to help the merge:\n\n", 321554427Speter 0); 321654427Speter cvs_output ("\t", 1); 321754427Speter cvs_output (program_name, 0); 321854427Speter if (CVSroot_cmdline != NULL) 321954427Speter { 322054427Speter cvs_output (" -d ", 0); 322154427Speter cvs_output (CVSroot_cmdline, 0); 322254427Speter } 322354427Speter cvs_output (" checkout -j", 0); 322454427Speter cvs_output (importmergecmd.mergetag1, 0); 322554427Speter cvs_output (" -j", 0); 322654427Speter cvs_output (importmergecmd.mergetag2, 0); 322754427Speter cvs_output (" ", 1); 322854427Speter cvs_output (importmergecmd.repository, 0); 322954427Speter cvs_output ("\n\n", 0); 323054427Speter 323154427Speter /* Clear the static variables so that everything is 323254427Speter ready for any subsequent importmergecmd tag. */ 323354427Speter importmergecmd.conflicts = 0; 323454427Speter free (importmergecmd.mergetag1); 323554427Speter importmergecmd.mergetag1 = NULL; 323654427Speter free (importmergecmd.mergetag2); 323754427Speter importmergecmd.mergetag2 = NULL; 323854427Speter free (importmergecmd.repository); 323954427Speter importmergecmd.repository = NULL; 324054427Speter 324154427Speter importmergecmd.seen = 0; 324254427Speter } 324332785Speter break; 324432785Speter default: 324532785Speter if (updated_seen) 324632785Speter { 324732785Speter if (strcmp (tag, "fname") == 0) 324832785Speter { 324932785Speter if (updated_fname != NULL) 325032785Speter { 325132785Speter /* Output the previous message now. This can happen 325232785Speter if there was no Update-existing or other such 325332785Speter response, due to the -n global option. */ 325432785Speter cvs_output ("U ", 0); 325532785Speter cvs_output (updated_fname, 0); 325632785Speter cvs_output ("\n", 1); 325732785Speter free (updated_fname); 325832785Speter } 325932785Speter updated_fname = xstrdup (text); 326032785Speter } 326132785Speter /* Swallow all other tags. Either they are extraneous 326232785Speter or they reflect future extensions that we can 326332785Speter safely ignore. */ 326432785Speter } 326554427Speter else if (importmergecmd.seen) 326654427Speter { 326754427Speter if (strcmp (tag, "conflicts") == 0) 3268175266Sobrien importmergecmd.conflicts = text ? atoi (text) : -1; 326954427Speter else if (strcmp (tag, "mergetag1") == 0) 327054427Speter importmergecmd.mergetag1 = xstrdup (text); 327154427Speter else if (strcmp (tag, "mergetag2") == 0) 327254427Speter importmergecmd.mergetag2 = xstrdup (text); 327354427Speter else if (strcmp (tag, "repository") == 0) 327454427Speter importmergecmd.repository = xstrdup (text); 327554427Speter /* Swallow all other tags. Either they are text for 327654427Speter which we are going to print our own version when we 327754427Speter see -importmergecmd, or they are future extensions 327854427Speter we can safely ignore. */ 327954427Speter } 328032785Speter else if (strcmp (tag, "newline") == 0) 328132785Speter printf ("\n"); 328232785Speter else if (text != NULL) 328332785Speter printf ("%s", text); 328432785Speter } 328532785Speter} 328632785Speter 328717721Speter#endif /* CLIENT_SUPPORT */ 328817721Speter#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT) 328917721Speter 329017721Speter/* This table must be writeable if the server code is included. */ 329117721Speterstruct response responses[] = 329217721Speter{ 329317721Speter#ifdef CLIENT_SUPPORT 329417721Speter#define RSP_LINE(n, f, t, s) {n, f, t, s} 329517721Speter#else /* ! CLIENT_SUPPORT */ 329617721Speter#define RSP_LINE(n, f, t, s) {n, s} 329717721Speter#endif /* CLIENT_SUPPORT */ 329817721Speter 329917721Speter RSP_LINE("ok", handle_ok, response_type_ok, rs_essential), 330017721Speter RSP_LINE("error", handle_error, response_type_error, rs_essential), 330117721Speter RSP_LINE("Valid-requests", handle_valid_requests, response_type_normal, 330217721Speter rs_essential), 330317721Speter RSP_LINE("Checked-in", handle_checked_in, response_type_normal, 330417721Speter rs_essential), 330517721Speter RSP_LINE("New-entry", handle_new_entry, response_type_normal, rs_optional), 330617721Speter RSP_LINE("Checksum", handle_checksum, response_type_normal, rs_optional), 330717721Speter RSP_LINE("Copy-file", handle_copy_file, response_type_normal, rs_optional), 330817721Speter RSP_LINE("Updated", handle_updated, response_type_normal, rs_essential), 330925839Speter RSP_LINE("Created", handle_created, response_type_normal, rs_optional), 331025839Speter RSP_LINE("Update-existing", handle_update_existing, response_type_normal, 331125839Speter rs_optional), 331217721Speter RSP_LINE("Merged", handle_merged, response_type_normal, rs_essential), 331317721Speter RSP_LINE("Patched", handle_patched, response_type_normal, rs_optional), 331425839Speter RSP_LINE("Rcs-diff", handle_rcs_diff, response_type_normal, rs_optional), 331517721Speter RSP_LINE("Mode", handle_mode, response_type_normal, rs_optional), 331626801Speter RSP_LINE("Mod-time", handle_mod_time, response_type_normal, rs_optional), 331717721Speter RSP_LINE("Removed", handle_removed, response_type_normal, rs_essential), 331817721Speter RSP_LINE("Remove-entry", handle_remove_entry, response_type_normal, 331917721Speter rs_optional), 332017721Speter RSP_LINE("Set-static-directory", handle_set_static_directory, 332117721Speter response_type_normal, 332217721Speter rs_optional), 332317721Speter RSP_LINE("Clear-static-directory", handle_clear_static_directory, 332417721Speter response_type_normal, 332517721Speter rs_optional), 332617721Speter RSP_LINE("Set-sticky", handle_set_sticky, response_type_normal, 332717721Speter rs_optional), 332817721Speter RSP_LINE("Clear-sticky", handle_clear_sticky, response_type_normal, 332917721Speter rs_optional), 333017721Speter RSP_LINE("Template", handle_template, response_type_normal, 333117721Speter rs_optional), 333217721Speter RSP_LINE("Notified", handle_notified, response_type_normal, rs_optional), 333317721Speter RSP_LINE("Module-expansion", handle_module_expansion, response_type_normal, 333417721Speter rs_optional), 333532896Speter RSP_LINE("Wrapper-rcsOption", handle_wrapper_rcs_option, 333632896Speter response_type_normal, 333732896Speter rs_optional), 333817721Speter RSP_LINE("M", handle_m, response_type_normal, rs_essential), 333932785Speter RSP_LINE("Mbinary", handle_mbinary, response_type_normal, rs_optional), 334017721Speter RSP_LINE("E", handle_e, response_type_normal, rs_essential), 334125839Speter RSP_LINE("F", handle_f, response_type_normal, rs_optional), 334232785Speter RSP_LINE("MT", handle_mt, response_type_normal, rs_optional), 334317721Speter /* Possibly should be response_type_error. */ 334417721Speter RSP_LINE(NULL, NULL, response_type_normal, rs_essential) 334517721Speter 334617721Speter#undef RSP_LINE 334717721Speter}; 334817721Speter 334917721Speter#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */ 335017721Speter#ifdef CLIENT_SUPPORT 335117721Speter 335217721Speter/* 335317721Speter * If LEN is 0, then send_to_server() computes string's length itself. 335417721Speter * 335517721Speter * Therefore, pass the real length when transmitting data that might 335617721Speter * contain 0's. 335717721Speter */ 335817721Spetervoid 335917721Spetersend_to_server (str, len) 3360128269Speter const char *str; 336117721Speter size_t len; 336217721Speter{ 336325839Speter static int nbytes; 336417721Speter 336525839Speter if (len == 0) 336625839Speter len = strlen (str); 336717721Speter 336825839Speter buf_output (to_server, str, len); 336925839Speter 337025839Speter /* There is no reason not to send data to the server, so do it 337125839Speter whenever we've accumulated enough information in the buffer to 337225839Speter make it worth sending. */ 337325839Speter nbytes += len; 337425839Speter if (nbytes >= 2 * BUFFER_DATA_SIZE) 337517721Speter { 337625839Speter int status; 337725839Speter 337825839Speter status = buf_send_output (to_server); 337925839Speter if (status != 0) 338025839Speter error (1, status, "error writing to server"); 338125839Speter nbytes = 0; 338217721Speter } 338317721Speter} 338417721Speter 338525839Speter/* Read up to LEN bytes from the server. Returns actual number of 338625839Speter bytes read, which will always be at least one; blocks if there is 338725839Speter no data available at all. Gives a fatal error on EOF or error. */ 338817721Speterstatic size_t 338917721Spetertry_read_from_server (buf, len) 339017721Speter char *buf; 339117721Speter size_t len; 339217721Speter{ 339325839Speter int status, nread; 339425839Speter char *data; 339517721Speter 339625839Speter status = buf_read_data (from_server, len, &data, &nread); 339725839Speter if (status != 0) 339817721Speter { 339925839Speter if (status == -1) 340017721Speter error (1, 0, 340117721Speter "end of file from server (consult above messages if any)"); 340225839Speter else if (status == -2) 340325839Speter error (1, 0, "out of memory"); 340425839Speter else 340525839Speter error (1, status, "reading from server"); 340617721Speter } 340717721Speter 340825839Speter memcpy (buf, data, nread); 340917721Speter 341017721Speter return nread; 341117721Speter} 341217721Speter 341317721Speter/* 341417721Speter * Read LEN bytes from the server or die trying. 341517721Speter */ 341617721Spetervoid 341717721Speterread_from_server (buf, len) 341817721Speter char *buf; 341917721Speter size_t len; 342017721Speter{ 342117721Speter size_t red = 0; 342217721Speter while (red < len) 342317721Speter { 342417721Speter red += try_read_from_server (buf + red, len - red); 342517721Speter if (red == len) 342617721Speter break; 342717721Speter } 342817721Speter} 342917721Speter 343017721Speter/* 343117721Speter * Get some server responses and process them. Returns nonzero for 343217721Speter * error, 0 for success. */ 343317721Speterint 343417721Speterget_server_responses () 343517721Speter{ 343617721Speter struct response *rs; 343717721Speter do 343817721Speter { 343917721Speter char *cmd; 344017721Speter int len; 344117721Speter 344225839Speter len = read_line (&cmd); 344317721Speter for (rs = responses; rs->name != NULL; ++rs) 344417721Speter if (strncmp (cmd, rs->name, strlen (rs->name)) == 0) 344517721Speter { 344617721Speter int cmdlen = strlen (rs->name); 344717721Speter if (cmd[cmdlen] == '\0') 344817721Speter ; 344917721Speter else if (cmd[cmdlen] == ' ') 345017721Speter ++cmdlen; 345117721Speter else 345217721Speter /* 345317721Speter * The first len characters match, but it's a different 345417721Speter * response. e.g. the response is "oklahoma" but we 345517721Speter * matched "ok". 345617721Speter */ 345717721Speter continue; 345817721Speter (*rs->func) (cmd + cmdlen, len - cmdlen); 345917721Speter break; 346017721Speter } 346117721Speter if (rs->name == NULL) 346217721Speter /* It's OK to print just to the first '\0'. */ 346332785Speter /* We might want to handle control characters and the like 346432785Speter in some other way other than just sending them to stdout. 346532785Speter One common reason for this error is if people use :ext: 346632785Speter with a version of rsh which is doing CRLF translation or 346732785Speter something, and so the client gets "ok^M" instead of "ok". 346832785Speter Right now that will tend to print part of this error 346932785Speter message over the other part of it. It seems like we could 347032785Speter do better (either in general, by quoting or omitting all 347132785Speter control characters, and/or specifically, by detecting the CRLF 347232785Speter case and printing a specific error message). */ 347317721Speter error (0, 0, 347417721Speter "warning: unrecognized response `%s' from cvs server", 347517721Speter cmd); 347617721Speter free (cmd); 347717721Speter } while (rs->type == response_type_normal); 347832785Speter 347932785Speter if (updated_fname != NULL) 348032785Speter { 348132785Speter /* Output the previous message now. This can happen 348232785Speter if there was no Update-existing or other such 348332785Speter response, due to the -n global option. */ 348432785Speter cvs_output ("U ", 0); 348532785Speter cvs_output (updated_fname, 0); 348632785Speter cvs_output ("\n", 1); 348732785Speter free (updated_fname); 348832785Speter updated_fname = NULL; 348932785Speter } 349032785Speter 349132785Speter if (rs->type == response_type_error) 349232785Speter return 1; 349332785Speter if (failure_exit) 349432785Speter return 1; 349532785Speter return 0; 349617721Speter} 349717721Speter 3498128269Speter 3499128269Speter 350017721Speter/* Get the responses and then close the connection. */ 350117721Speter 350217721Speter/* 350317721Speter * Flag var; we'll set it in start_server() and not one of its 350417721Speter * callees, such as start_rsh_server(). This means that there might 350517721Speter * be a small window between the starting of the server and the 350617721Speter * setting of this var, but all the code in that window shouldn't care 350717721Speter * because it's busy checking return values to see if the server got 350817721Speter * started successfully anyway. 350917721Speter */ 351017721Speterint server_started = 0; 351117721Speter 351217721Speterint 351317721Speterget_responses_and_close () 351417721Speter{ 351517721Speter int errs = get_server_responses (); 351625839Speter int status; 351717721Speter 3518128269Speter /* The following is necessary when working with multiple cvsroots, at least 3519128269Speter * with commit. It used to be buried nicely in do_deferred_progs() before 3520128269Speter * that function was removed. I suspect it wouldn't be necessary if 3521128269Speter * call_in_directory() saved its working directory via save_cwd() before 3522128269Speter * changing its directory and restored the saved working directory via 3523128269Speter * restore_cwd() before exiting. Of course, calling CVS_CHDIR only once, 3524128269Speter * here, may be more efficient. 3525128269Speter */ 3526128269Speter if( toplevel_wd != NULL ) 352725839Speter { 3528128269Speter if( CVS_CHDIR( toplevel_wd ) < 0 ) 3529128269Speter error( 1, errno, "could not chdir to %s", toplevel_wd ); 353025839Speter } 353125839Speter 353217721Speter if (client_prune_dirs) 353317721Speter process_prune_candidates (); 353417721Speter 3535102843Speter /* First we shut down TO_SERVER. That tells the server that its input is 3536102843Speter * finished. It then shuts down the buffer it is sending to us, at which 3537102843Speter * point our shut down of FROM_SERVER will complete. 3538102843Speter */ 353925839Speter 354025839Speter status = buf_shutdown (to_server); 354125839Speter if (status != 0) 3542102843Speter error (0, status, "shutting down buffer to server"); 3543107487Speter buf_free (to_server); 3544107487Speter to_server = NULL; 3545107487Speter 354625839Speter status = buf_shutdown (from_server); 354725839Speter if (status != 0) 354825839Speter error (0, status, "shutting down buffer from server"); 354966528Speter buf_free (from_server); 3550107487Speter from_server = NULL; 355117721Speter server_started = 0; 355217721Speter 355366528Speter /* see if we need to sleep before returning to avoid time-stamp races */ 355425839Speter if (last_register_time) 355525839Speter { 355681407Speter sleep_past (last_register_time); 355725839Speter } 355825839Speter 355917721Speter return errs; 356017721Speter} 356117721Speter 356232785Speter#ifndef NO_EXT_METHOD 3563102843Speterstatic void start_rsh_server PROTO((cvsroot_t *, struct buffer **, struct buffer **)); 356432785Speter#endif 356517721Speter 356617721Speterint 356717721Spetersupported_request (name) 356825839Speter char *name; 356917721Speter{ 357025839Speter struct request *rq; 357117721Speter 357225839Speter for (rq = requests; rq->name; rq++) 357325839Speter if (!strcmp (rq->name, name)) 357454427Speter return (rq->flags & RQ_SUPPORTED) != 0; 357525839Speter error (1, 0, "internal error: testing support for unknown option?"); 357625839Speter /* NOTREACHED */ 357725839Speter return 0; 357817721Speter} 357917721Speter 358081407Speter 358181407Speter 3582128269Speter#if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI) 358332785Speterstatic struct hostent *init_sockaddr PROTO ((struct sockaddr_in *, char *, 358432785Speter unsigned int)); 358525839Speter 358632785Speterstatic struct hostent * 358717721Speterinit_sockaddr (name, hostname, port) 358817721Speter struct sockaddr_in *name; 358925839Speter char *hostname; 359025839Speter unsigned int port; 359117721Speter{ 359217721Speter struct hostent *hostinfo; 359332785Speter unsigned short shortport = port; 359417721Speter 359517721Speter memset (name, 0, sizeof (*name)); 359617721Speter name->sin_family = AF_INET; 359725839Speter name->sin_port = htons (shortport); 359817721Speter hostinfo = gethostbyname (hostname); 359917721Speter if (hostinfo == NULL) 360017721Speter { 360117721Speter fprintf (stderr, "Unknown host %s.\n", hostname); 360225839Speter error_exit (); 360317721Speter } 360417721Speter name->sin_addr = *(struct in_addr *) hostinfo->h_addr; 360532785Speter return hostinfo; 360617721Speter} 360717721Speter 360817721Speter 360981407Speter 361081407Speter/* Generic function to do port number lookup tasks. 361181407Speter * 361281407Speter * In order of precedence, will return: 361381407Speter * getenv (envname), if defined 361481407Speter * getservbyname (portname), if defined 361581407Speter * defaultport 361681407Speter */ 361725839Speterstatic int 361881407Speterget_port_number (envname, portname, defaultport) 361981407Speter const char *envname; 362081407Speter const char *portname; 362181407Speter int defaultport; 362217721Speter{ 362381407Speter struct servent *s; 362481407Speter char *port_s; 362525839Speter 362681407Speter if (envname && (port_s = getenv (envname))) 362781407Speter { 362881407Speter int port = atoi (port_s); 362981407Speter if (port <= 0) 363081407Speter { 363181407Speter error (0, 0, "%s must be a positive integer! If you", envname); 363281407Speter error (0, 0, "are trying to force a connection via rsh, please"); 363381407Speter error (0, 0, "put \":server:\" at the beginning of your CVSROOT"); 363481407Speter error (1, 0, "variable."); 363581407Speter } 363681407Speter return port; 363781407Speter } 363881407Speter else if (portname && (s = getservbyname (portname, "tcp"))) 363925839Speter return ntohs (s->s_port); 364025839Speter else 364181407Speter return defaultport; 364217721Speter} 364317721Speter 364417721Speter 364581407Speter 364681407Speter/* get the port number for a client to connect to based on the port 364781407Speter * and method of a cvsroot_t. 364881407Speter * 364981407Speter * we do this here instead of in parse_cvsroot so that we can keep network 365081407Speter * code confined to a localized area and also to delay the lookup until the 365181407Speter * last possible moment so it remains possible to run cvs client commands that 3652107487Speter * skip opening connections to the server (i.e. skip network operations 3653107487Speter * entirely) 365481407Speter * 3655107487Speter * and yes, I know none of the commands do that now, but here's to planning 365681407Speter * for the future, eh? cheers. 365781407Speter * 365881407Speter * FIXME - We could cache the port lookup safely right now as we never change 365981407Speter * it for a single root on the fly, but we'd have to un'const some other 3660107487Speter * functions - REMOVE_FIXME? This may be unecessary. We're talking about, 3661107487Speter * what, usually one, sometimes two lookups of the port per invocation. I 3662107487Speter * think twice is by far the rarer of the two cases - only the login function 3663107487Speter * will need to do it to save the canonical CVSROOT. -DRP 366481407Speter */ 366581407Speterint 366681407Speterget_cvs_port_number (root) 366781407Speter const cvsroot_t *root; 366881407Speter{ 366981407Speter 367081407Speter if (root->port) return root->port; 367181407Speter 367281407Speter switch (root->method) 367381407Speter { 3674128269Speter# ifdef HAVE_GSSAPI 367581407Speter case gserver_method: 3676128269Speter# endif /* HAVE_GSSAPI */ 3677128269Speter# ifdef AUTH_CLIENT_SUPPORT 367881407Speter case pserver_method: 3679128269Speter# endif /* AUTH_CLIENT_SUPPORT */ 3680128269Speter# if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI) 368181407Speter return get_port_number ("CVS_CLIENT_PORT", "cvspserver", CVS_AUTH_PORT); 3682128269Speter# endif /* defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI) */ 3683128269Speter# ifdef HAVE_KERBEROS 368481407Speter case kserver_method: 368581407Speter return get_port_number ("CVS_CLIENT_PORT", "cvs", CVS_PORT); 3686128269Speter# endif /* HAVE_KERBEROS */ 368781407Speter default: 368881407Speter error(1, EINVAL, "internal error: get_cvs_port_number called for invalid connection method (%s)", 368981407Speter method_names[root->method]); 369081407Speter break; 369181407Speter } 3692102843Speter /* NOTREACHED */ 3693102843Speter return -1; 369481407Speter} 369581407Speter 369681407Speter 369781407Speter 3698102843Spetervoid 3699102843Spetermake_bufs_from_fds (tofd, fromfd, child_pid, to_server, from_server, is_sock) 3700102843Speter int tofd; 3701102843Speter int fromfd; 3702102843Speter int child_pid; 3703102843Speter struct buffer **to_server; 3704102843Speter struct buffer **from_server; 3705102843Speter int is_sock; 370632785Speter{ 3707102843Speter FILE *to_server_fp; 3708102843Speter FILE *from_server_fp; 370932785Speter 3710128269Speter# ifdef NO_SOCKET_TO_FD 3711102843Speter if (is_sock) 371232785Speter { 3713102843Speter assert (tofd == fromfd); 3714102843Speter *to_server = socket_buffer_initialize (tofd, 0, 3715102843Speter (BUFMEMERRPROC) NULL); 3716102843Speter *from_server = socket_buffer_initialize (tofd, 1, 3717102843Speter (BUFMEMERRPROC) NULL); 3718102843Speter } 3719102843Speter else 3720128269Speter# endif /* NO_SOCKET_TO_FD */ 3721102843Speter { 3722102843Speter /* todo: some OS's don't need these calls... */ 3723102843Speter close_on_exec (tofd); 3724102843Speter close_on_exec (fromfd); 372532785Speter 3726102843Speter /* SCO 3 and AIX have a nasty bug in the I/O libraries which precludes 3727102843Speter fdopening the same file descriptor twice, so dup it if it is the 3728102843Speter same. */ 3729102843Speter if (tofd == fromfd) 373032785Speter { 3731102843Speter fromfd = dup (tofd); 3732102843Speter if (fromfd < 0) 3733102843Speter error (1, errno, "cannot dup net connection"); 373432785Speter } 3735102843Speter 3736102843Speter /* These will use binary mode on systems which have it. */ 3737102843Speter /* 3738102843Speter * Also, we know that from_server is shut down second, so we pass 3739102843Speter * child_pid in there. In theory, it should be stored in both 3740102843Speter * buffers with a ref count... 3741102843Speter */ 3742102843Speter to_server_fp = fdopen (tofd, FOPEN_BINARY_WRITE); 3743102843Speter if (to_server_fp == NULL) 3744102843Speter error (1, errno, "cannot fdopen %d for write", tofd); 3745102843Speter *to_server = stdio_buffer_initialize (to_server_fp, 0, 0, 3746102843Speter (BUFMEMERRPROC) NULL); 3747102843Speter 3748102843Speter from_server_fp = fdopen (fromfd, FOPEN_BINARY_READ); 3749102843Speter if (from_server_fp == NULL) 3750102843Speter error (1, errno, "cannot fdopen %d for read", fromfd); 3751102843Speter *from_server = stdio_buffer_initialize (from_server_fp, child_pid, 1, 3752102843Speter (BUFMEMERRPROC) NULL); 375332785Speter } 3754102843Speter} 3755128269Speter#endif /* defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_KERBEROS) || defined(HAVE_GSSAPI) */ 375632785Speter 375732785Speter 375832785Speter 3759128269Speter#if defined (AUTH_CLIENT_SUPPORT) || defined(HAVE_GSSAPI) 376032785Speter/* Connect to the authenticating server. 376132785Speter 376232785Speter If VERIFY_ONLY is non-zero, then just verify that the password is 376332785Speter correct and then shutdown the connection. 376432785Speter 376532785Speter If VERIFY_ONLY is 0, then really connect to the server. 376632785Speter 376732785Speter If DO_GSSAPI is non-zero, then we use GSSAPI authentication rather 376832785Speter than the pserver password authentication. 376932785Speter 377032785Speter If we fail to connect or if access is denied, then die with fatal 377132785Speter error. */ 377232785Spetervoid 3773102843Speterconnect_to_pserver (root, to_server_p, from_server_p, verify_only, do_gssapi) 3774102843Speter cvsroot_t *root; 3775102843Speter struct buffer **to_server_p; 3776102843Speter struct buffer **from_server_p; 3777102843Speter int verify_only; 3778102843Speter int do_gssapi; 377917721Speter{ 378017721Speter int sock; 378117721Speter int port_number; 378217721Speter struct sockaddr_in client_sai; 378332785Speter struct hostent *hostinfo; 3784102843Speter struct buffer *to_server, *from_server; 378517721Speter 378617721Speter sock = socket (AF_INET, SOCK_STREAM, 0); 378717721Speter if (sock == -1) 378817721Speter { 378932785Speter error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO)); 379017721Speter } 3791102843Speter port_number = get_cvs_port_number (root); 3792102843Speter hostinfo = init_sockaddr (&client_sai, root->hostname, port_number); 379381407Speter if (trace) 379481407Speter { 379581407Speter fprintf (stderr, " -> Connecting to %s(%s):%d\n", 3796102843Speter root->hostname, 379781407Speter inet_ntoa (client_sai.sin_addr), port_number); 379881407Speter } 379917721Speter if (connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai)) 380017721Speter < 0) 380181407Speter error (1, 0, "connect to %s(%s):%d failed: %s", 3802102843Speter root->hostname, 380381407Speter inet_ntoa (client_sai.sin_addr), 380425839Speter port_number, SOCK_STRERROR (SOCK_ERRNO)); 380517721Speter 3806102843Speter make_bufs_from_fds (sock, sock, 0, &to_server, &from_server, 1); 3807102843Speter 3808102843Speter auth_server (root, to_server, from_server, verify_only, do_gssapi, hostinfo); 3809102843Speter 3810102843Speter if (verify_only) 3811102843Speter { 3812102843Speter int status; 3813102843Speter 3814102843Speter status = buf_shutdown (to_server); 3815102843Speter if (status != 0) 3816102843Speter error (0, status, "shutting down buffer to server"); 3817107487Speter buf_free (to_server); 3818107487Speter to_server = NULL; 3819107487Speter 3820102843Speter status = buf_shutdown (from_server); 3821102843Speter if (status != 0) 3822102843Speter error (0, status, "shutting down buffer from server"); 3823102843Speter buf_free (from_server); 3824107487Speter from_server = NULL; 3825102843Speter 3826102843Speter /* Don't need to set server_started = 0 since we don't set it to 1 3827102843Speter * until returning from this call. 3828102843Speter */ 3829102843Speter } 3830102843Speter else 3831102843Speter { 3832102843Speter *to_server_p = to_server; 3833102843Speter *from_server_p = from_server; 3834102843Speter } 3835102843Speter 3836102843Speter return; 3837102843Speter} 3838102843Speter 3839102843Speter 3840102843Speter 3841102843Speterstatic void 3842102843Speterauth_server (root, lto_server, lfrom_server, verify_only, do_gssapi, hostinfo) 3843102843Speter cvsroot_t *root; 3844102843Speter struct buffer *lto_server; 3845102843Speter struct buffer *lfrom_server; 3846102843Speter int verify_only; 3847102843Speter int do_gssapi; 3848102843Speter struct hostent *hostinfo; 3849102843Speter{ 3850177394Sobrien char *username = ""; /* the username we use to connect */ 3851102843Speter char no_passwd = 0; /* gets set if no password found */ 3852102843Speter 3853102843Speter /* FIXME!!!!!!!!!!!!!!!!!! 3854102843Speter * 3855102843Speter * THIS IS REALLY UGLY! 3856102843Speter * 3857102843Speter * I'm setting the globals here so we can make calls to send_to_server & 3858102843Speter * read_line. This happens again _after_ we return if we're not in 3859102843Speter * verify_only mode. We should be relying on the values we passed in, but 3860102843Speter * sent_to_server and read_line don't require an outside buf yet. 3861102843Speter */ 3862102843Speter to_server = lto_server; 3863102843Speter from_server = lfrom_server; 3864102843Speter 386517721Speter /* Run the authorization mini-protocol before anything else. */ 386632785Speter if (do_gssapi) 386717721Speter { 3868128269Speter# ifdef HAVE_GSSAPI 3869107487Speter FILE *fp = stdio_buffer_get_file(lto_server); 3870107487Speter int fd = fp ? fileno(fp) : -1; 3871102843Speter struct stat s; 3872102843Speter 3873107487Speter if ((fd < 0) || (fstat (fd, &s) < 0) || !S_ISSOCK(s.st_mode)) 387481407Speter { 3875102843Speter error (1, 0, "gserver currently only enabled for socket connections"); 3876102843Speter } 3877102843Speter 3878102843Speter if (! connect_to_gserver (root, fd, hostinfo)) 3879102843Speter { 3880102843Speter error (1, 0, 388181407Speter "authorization failed: server %s rejected access to %s", 3882102843Speter root->hostname, root->directory); 388381407Speter } 3884128269Speter# else /* ! HAVE_GSSAPI */ 3885128269Speter error (1, 0, "INTERNAL ERROR: This client does not support GSSAPI authentication"); 3886128269Speter# endif /* HAVE_GSSAPI */ 388732785Speter } 3888128269Speter else /* ! do_gssapi */ 388932785Speter { 3890128269Speter# ifdef AUTH_CLIENT_SUPPORT 389117721Speter char *begin = NULL; 389217721Speter char *password = NULL; 389317721Speter char *end = NULL; 389481407Speter 389517721Speter if (verify_only) 389617721Speter { 3897102843Speter begin = "BEGIN VERIFICATION REQUEST"; 3898102843Speter end = "END VERIFICATION REQUEST"; 389917721Speter } 390017721Speter else 390117721Speter { 3902102843Speter begin = "BEGIN AUTH REQUEST"; 3903102843Speter end = "END AUTH REQUEST"; 390417721Speter } 390517721Speter 390617721Speter /* Get the password, probably from ~/.cvspass. */ 390725839Speter password = get_cvs_password (); 3908102843Speter username = root->username ? root->username : getcaller(); 390981407Speter 3910102843Speter /* Send the empty string by default. This is so anonymous CVS 3911102843Speter access doesn't require client to have done "cvs login". */ 3912102843Speter if (password == NULL) 3913102843Speter { 3914102843Speter no_passwd = 1; 3915102843Speter password = scramble (""); 3916102843Speter } 391717721Speter 391817721Speter /* Announce that we're starting the authorization protocol. */ 3919102843Speter send_to_server(begin, 0); 3920102843Speter send_to_server("\012", 1); 392117721Speter 392217721Speter /* Send the data the server needs. */ 3923102843Speter send_to_server(root->directory, 0); 3924102843Speter send_to_server("\012", 1); 3925102843Speter send_to_server(username, 0); 3926102843Speter send_to_server("\012", 1); 3927102843Speter send_to_server(password, 0); 3928102843Speter send_to_server("\012", 1); 392917721Speter 393017721Speter /* Announce that we're ending the authorization protocol. */ 3931102843Speter send_to_server(end, 0); 3932102843Speter send_to_server("\012", 1); 393317721Speter 3934177394Sobrien free_cvs_password (password); 3935177394Sobrien password = NULL; 3936128269Speter# else /* ! AUTH_CLIENT_SUPPORT */ 3937128269Speter error (1, 0, "INTERNAL ERROR: This client does not support pserver authentication"); 3938128269Speter# endif /* AUTH_CLIENT_SUPPORT */ 3939128269Speter } /* if (do_gssapi) */ 394017721Speter 394132785Speter { 394232785Speter char *read_buf; 394332785Speter 394432785Speter /* Loop, getting responses from the server. */ 394532785Speter while (1) 394617721Speter { 3947102843Speter read_line (&read_buf); 394817721Speter 394932785Speter if (strcmp (read_buf, "I HATE YOU") == 0) 395032785Speter { 395181407Speter /* Authorization not granted. 395281407Speter * 395381407Speter * This is a little confusing since we can reach this while loop in GSSAPI 395481407Speter * mode, but if GSSAPI authentication failed, we already jumped to the 395581407Speter * rejected label (there is no case where the connect_to_gserver function 395681407Speter * can return 1 and we will not receive "I LOVE YOU" from the server, barring 395781407Speter * broken connections and garbled messages, of course). 395881407Speter * 3959102843Speter * i.e. This is a pserver specific error message and should be since 396081407Speter * GSSAPI doesn't use username. 396181407Speter */ 396281407Speter error (0, 0, 396381407Speter "authorization failed: server %s rejected access to %s for user %s", 3964102843Speter root->hostname, root->directory, username); 396581407Speter 396681407Speter /* Output a special error message if authentication was attempted 396781407Speter with no password -- the user should be made aware that they may 396881407Speter have missed a step. */ 396981407Speter if (no_passwd) 397081407Speter { 397181407Speter error (0, 0, 397281407Speter "used empty password; try \"cvs login\" with a real password"); 397381407Speter } 3974102843Speter error_exit(); 397532785Speter } 397632785Speter else if (strncmp (read_buf, "E ", 2) == 0) 397732785Speter { 397832785Speter fprintf (stderr, "%s\n", read_buf + 2); 397917721Speter 398032785Speter /* Continue with the authentication protocol. */ 398132785Speter } 398232785Speter else if (strncmp (read_buf, "error ", 6) == 0) 398317721Speter { 398432785Speter char *p; 398532785Speter 398632785Speter /* First skip the code. */ 398732785Speter p = read_buf + 6; 398832785Speter while (*p != ' ' && *p != '\0') 398932785Speter ++p; 399032785Speter 399132785Speter /* Skip the space that follows the code. */ 399232785Speter if (*p == ' ') 399332785Speter ++p; 399432785Speter 399532785Speter /* Now output the text. */ 399632785Speter fprintf (stderr, "%s\n", p); 3997102843Speter error_exit(); 399817721Speter } 399932785Speter else if (strcmp (read_buf, "I LOVE YOU") == 0) 400032785Speter { 400132785Speter free (read_buf); 400232785Speter break; 400332785Speter } 400417721Speter else 400532785Speter { 400617721Speter error (1, 0, 400717721Speter "unrecognized auth response from %s: %s", 4008102843Speter root->hostname, read_buf); 400917721Speter } 401032785Speter free (read_buf); 401117721Speter } 401217721Speter } 401317721Speter} 4014128269Speter#endif /* defined (AUTH_CLIENT_SUPPORT) || defined(HAVE_GSSAPI) */ 401517721Speter 401617721Speter 401781407Speter 4018128269Speter#ifdef CLIENT_SUPPORT 4019128269Speter/* void 4020128269Speter * connect_to_forked_server ( struct buffer **to_server, 4021128269Speter * struct buffer **from_server ) 4022128269Speter * 4023128269Speter * Connect to a forked server process. 4024128269Speter */ 4025128269Spetervoid 4026128269Speterconnect_to_forked_server (to_server, from_server) 4027128269Speter struct buffer **to_server; 4028128269Speter struct buffer **from_server; 4029128269Speter{ 4030128269Speter int tofd, fromfd; 4031128269Speter int child_pid; 4032128269Speter 4033128269Speter /* This is pretty simple. All we need to do is choose the correct 4034128269Speter cvs binary and call piped_child. */ 4035128269Speter 4036128269Speter const char *command[3]; 4037128269Speter 4038128269Speter command[0] = getenv ("CVS_SERVER"); 4039128269Speter if (! command[0]) 4040128269Speter command[0] = program_path; 4041128269Speter 4042128269Speter command[1] = "server"; 4043128269Speter command[2] = NULL; 4044128269Speter 4045128269Speter if (trace) 4046128269Speter { 4047128269Speter fprintf (stderr, " -> Forking server: %s %s\n", command[0], command[1]); 4048128269Speter } 4049128269Speter 4050175266Sobrien child_pid = piped_child (command, &tofd, &fromfd, 0); 4051128269Speter if (child_pid < 0) 4052128269Speter error (1, 0, "could not fork server process"); 4053128269Speter 4054128269Speter make_bufs_from_fds (tofd, fromfd, child_pid, to_server, from_server, 0); 4055128269Speter} 4056128269Speter#endif /* CLIENT_SUPPORT */ 4057128269Speter 4058128269Speter 4059128269Speter 406081407Speter#ifdef HAVE_KERBEROS 406132785Speter/* This function has not been changed to deal with NO_SOCKET_TO_FD 406232785Speter (i.e., systems on which sockets cannot be converted to file 406332785Speter descriptors). The first person to try building a kerberos client 406432785Speter on such a system (OS/2, Windows 95, and maybe others) will have to 4065102843Speter take care of this. */ 406617721Spetervoid 4067102843Speterstart_tcp_server (root, to_server, from_server) 4068102843Speter cvsroot_t *root; 4069102843Speter struct buffer **to_server; 4070102843Speter struct buffer **from_server; 407117721Speter{ 407232785Speter int s; 407325839Speter const char *portenv; 407425839Speter int port; 407532785Speter struct hostent *hp; 407625839Speter struct sockaddr_in sin; 407732785Speter char *hname; 407817721Speter 407932785Speter s = socket (AF_INET, SOCK_STREAM, 0); 408032785Speter if (s < 0) 408132785Speter error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO)); 408217721Speter 4083102843Speter port = get_cvs_port_number (root); 408425839Speter 4085102843Speter hp = init_sockaddr (&sin, root->hostname, port); 408625839Speter 4087128269Speter hname = xstrdup (hp->h_name); 408832785Speter 408981407Speter if (trace) 409081407Speter { 409181407Speter fprintf (stderr, " -> Connecting to %s(%s):%d\n", 4092102843Speter root->hostname, 409381954Speter inet_ntoa (sin.sin_addr), port); 409481407Speter } 409581407Speter 409632785Speter if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0) 409781407Speter error (1, 0, "connect to %s(%s):%d failed: %s", 4098102843Speter root->hostname, 409981954Speter inet_ntoa (sin.sin_addr), 410032785Speter port, SOCK_STRERROR (SOCK_ERRNO)); 410125839Speter 410217721Speter { 410332785Speter const char *realm; 410425839Speter struct sockaddr_in laddr; 410525839Speter int laddrlen; 410632785Speter KTEXT_ST ticket; 410725839Speter MSG_DAT msg_data; 410825839Speter CREDENTIALS cred; 410932785Speter int status; 411025839Speter 411132785Speter realm = krb_realmofhost (hname); 411232785Speter 411325839Speter laddrlen = sizeof (laddr); 411425839Speter if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0) 411532785Speter error (1, 0, "getsockname failed: %s", SOCK_STRERROR (SOCK_ERRNO)); 411625839Speter 411725839Speter /* We don't care about the checksum, and pass it as zero. */ 411825839Speter status = krb_sendauth (KOPT_DO_MUTUAL, s, &ticket, "rcmd", 411925839Speter hname, realm, (unsigned long) 0, &msg_data, 412025839Speter &cred, sched, &laddr, &sin, "KCVSV1.0"); 412125839Speter if (status != KSUCCESS) 412232785Speter error (1, 0, "kerberos authentication failed: %s", 412332785Speter krb_get_err_text (status)); 412432785Speter memcpy (kblock, cred.session, sizeof (C_Block)); 412532785Speter } 412625839Speter 4127102843Speter close_on_exec (s); 412832785Speter 412932785Speter free (hname); 413032785Speter 413132785Speter /* Give caller the values it wants. */ 4132102843Speter make_bufs_from_fds (s, s, 0, to_server, from_server, 1); 413332785Speter} 413432785Speter 413517721Speter#endif /* HAVE_KERBEROS */ 413617721Speter 413732785Speter#ifdef HAVE_GSSAPI 413817721Speter 413932785Speter/* Receive a given number of bytes. */ 414032785Speter 414132785Speterstatic void 414232785Speterrecv_bytes (sock, buf, need) 414332785Speter int sock; 414432785Speter char *buf; 414532785Speter int need; 414632785Speter{ 414732785Speter while (need > 0) 414832785Speter { 414932785Speter int got; 415032785Speter 415132785Speter got = recv (sock, buf, need, 0); 415281407Speter if (got <= 0) 415381407Speter error (1, 0, "recv() from server %s: %s", current_parsed_root->hostname, 415481407Speter got == 0 ? "EOF" : SOCK_STRERROR (SOCK_ERRNO)); 415581407Speter 415632785Speter buf += got; 415732785Speter need -= got; 415817721Speter } 415932785Speter} 416032785Speter 416132785Speter/* Connect to the server using GSSAPI authentication. */ 416232785Speter 4163102843Speter/* FIXME 4164102843Speter * 4165102843Speter * This really needs to be rewritten to use a buffer and not a socket. 4166102843Speter * This would enable gserver to work with the SSL code I'm about to commit 4167102843Speter * since the SSL connection is going to look like a FIFO and not a socket. 4168102843Speter * 4169102843Speter * I think, basically, it will need to use buf_output and buf_read directly 4170102843Speter * since I don't think there is a read_bytes function - only read_line. 4171102843Speter * 4172102843Speter * recv_bytes could then be removed too. 4173102843Speter * 4174102843Speter * Besides, I added some cruft to reenable the socket which shouldn't be 4175102843Speter * there. This would also enable its removal. 4176102843Speter */ 4177102843Speter#define BUFSIZE 1024 417832785Speterstatic int 4179102843Speterconnect_to_gserver (root, sock, hostinfo) 4180102843Speter cvsroot_t *root; 4181102843Speter int sock; 4182102843Speter struct hostent *hostinfo; 418332785Speter{ 418432785Speter char *str; 4185102843Speter char buf[BUFSIZE]; 418632785Speter gss_buffer_desc *tok_in_ptr, tok_in, tok_out; 418732785Speter OM_uint32 stat_min, stat_maj; 418832785Speter gss_name_t server_name; 418932785Speter 419032785Speter str = "BEGIN GSSAPI REQUEST\012"; 419132785Speter 419232785Speter if (send (sock, str, strlen (str), 0) < 0) 419332785Speter error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO)); 419432785Speter 4195102843Speter if (strlen (hostinfo->h_name) > BUFSIZE - 5) 4196102843Speter error (1, 0, "Internal error: hostname exceeds length of buffer"); 419732785Speter sprintf (buf, "cvs@%s", hostinfo->h_name); 419832785Speter tok_in.length = strlen (buf); 419932785Speter tok_in.value = buf; 420044852Speter gss_import_name (&stat_min, &tok_in, GSS_C_NT_HOSTBASED_SERVICE, 420144852Speter &server_name); 420232785Speter 420332785Speter tok_in_ptr = GSS_C_NO_BUFFER; 420432785Speter gcontext = GSS_C_NO_CONTEXT; 420532785Speter 420632785Speter do 420717721Speter { 420832785Speter stat_maj = gss_init_sec_context (&stat_min, GSS_C_NO_CREDENTIAL, 420932785Speter &gcontext, server_name, 421032785Speter GSS_C_NULL_OID, 421132785Speter (GSS_C_MUTUAL_FLAG 421232785Speter | GSS_C_REPLAY_FLAG), 421332785Speter 0, NULL, tok_in_ptr, NULL, &tok_out, 421432785Speter NULL, NULL); 421532785Speter if (stat_maj != GSS_S_COMPLETE && stat_maj != GSS_S_CONTINUE_NEEDED) 421632785Speter { 421732785Speter OM_uint32 message_context; 421866528Speter OM_uint32 new_stat_min; 421932785Speter 422032785Speter message_context = 0; 422166528Speter gss_display_status (&new_stat_min, stat_maj, GSS_C_GSS_CODE, 422266528Speter GSS_C_NULL_OID, &message_context, &tok_out); 422366528Speter error (0, 0, "GSSAPI authentication failed: %s", 422466528Speter (char *) tok_out.value); 422566528Speter 422666528Speter message_context = 0; 422766528Speter gss_display_status (&new_stat_min, stat_min, GSS_C_MECH_CODE, 422832785Speter GSS_C_NULL_OID, &message_context, &tok_out); 422932785Speter error (1, 0, "GSSAPI authentication failed: %s", 423032785Speter (char *) tok_out.value); 423132785Speter } 423232785Speter 423332785Speter if (tok_out.length == 0) 423432785Speter { 423532785Speter tok_in.length = 0; 423632785Speter } 423732785Speter else 423832785Speter { 423932785Speter char cbuf[2]; 424032785Speter int need; 424132785Speter 424232785Speter cbuf[0] = (tok_out.length >> 8) & 0xff; 424332785Speter cbuf[1] = tok_out.length & 0xff; 424432785Speter if (send (sock, cbuf, 2, 0) < 0) 424532785Speter error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO)); 424632785Speter if (send (sock, tok_out.value, tok_out.length, 0) < 0) 424732785Speter error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO)); 424832785Speter 424932785Speter recv_bytes (sock, cbuf, 2); 425032785Speter need = ((cbuf[0] & 0xff) << 8) | (cbuf[1] & 0xff); 425166528Speter 425266528Speter if (need > sizeof buf) 425366528Speter { 4254175266Sobrien ssize_t got; 4255175266Sobrien size_t total; 425666528Speter 425766528Speter /* This usually means that the server sent us an error 425866528Speter message. Read it byte by byte and print it out. 425966528Speter FIXME: This is a terrible error handling strategy. 426066528Speter However, even if we fix the server, we will still 426166528Speter want to do this to work with older servers. */ 426266528Speter buf[0] = cbuf[0]; 426366528Speter buf[1] = cbuf[1]; 4264175266Sobrien total = 2; 4265175266Sobrien while (got = recv (sock, buf + total, sizeof buf - total, 0)) 4266175266Sobrien { 4267175266Sobrien if (got < 0) 4268175266Sobrien error (1, 0, "recv() from server %s: %s", 4269175266Sobrien root->hostname, SOCK_STRERROR (SOCK_ERRNO)); 4270175266Sobrien total += got; 4271175266Sobrien if (strrchr (buf + total - got, '\n')) 4272175266Sobrien break; 4273175266Sobrien } 4274175266Sobrien buf[total] = '\0'; 4275175266Sobrien if (buf[total - 1] == '\n') 4276175266Sobrien buf[total - 1] = '\0'; 4277102843Speter error (1, 0, "error from server %s: %s", root->hostname, 427866528Speter buf); 427966528Speter } 428066528Speter 428132785Speter recv_bytes (sock, buf, need); 428232785Speter tok_in.length = need; 428332785Speter } 428432785Speter 428532785Speter tok_in.value = buf; 428632785Speter tok_in_ptr = &tok_in; 428717721Speter } 428832785Speter while (stat_maj == GSS_S_CONTINUE_NEEDED); 428917721Speter 429032785Speter return 1; 429117721Speter} 429217721Speter 429332785Speter#endif /* HAVE_GSSAPI */ 429417721Speter 4295128269Speter 4296128269Speter 429717721Speterstatic int send_variable_proc PROTO ((Node *, void *)); 429817721Speter 429917721Speterstatic int 430017721Spetersend_variable_proc (node, closure) 430117721Speter Node *node; 430217721Speter void *closure; 430317721Speter{ 430417721Speter send_to_server ("Set ", 0); 430517721Speter send_to_server (node->key, 0); 430617721Speter send_to_server ("=", 1); 430717721Speter send_to_server (node->data, 0); 430817721Speter send_to_server ("\012", 1); 430917721Speter return 0; 431017721Speter} 431117721Speter 4312128269Speter 4313128269Speter 431417721Speter/* Contact the server. */ 431517721Spetervoid 431617721Speterstart_server () 431717721Speter{ 4318102843Speter int rootless; 431925839Speter char *log = getenv ("CVS_CLIENT_LOG"); 432017721Speter 432154427Speter /* Clear our static variables for this invocation. */ 432254427Speter if (toplevel_repos != NULL) 432354427Speter free (toplevel_repos); 432454427Speter toplevel_repos = NULL; 432554427Speter 432625839Speter /* Note that generally speaking we do *not* fall back to a different 432725839Speter way of connecting if the first one does not work. This is slow 432825839Speter (*really* slow on a 14.4kbps link); the clean way to have a CVS 432925839Speter which supports several ways of connecting is with access methods. */ 433017721Speter 433181407Speter switch (current_parsed_root->method) 433225839Speter { 433317721Speter 433417721Speter#ifdef AUTH_CLIENT_SUPPORT 433525839Speter case pserver_method: 4336102843Speter /* Toss the return value. It will die with an error message if 4337102843Speter * anything goes wrong anyway. 4338102843Speter */ 4339102843Speter connect_to_pserver (current_parsed_root, &to_server, &from_server, 0, 0); 434025839Speter break; 4341128269Speter#endif /* AUTH_CLIENT_SUPPORT */ 434217721Speter 434325839Speter#if HAVE_KERBEROS 434425839Speter case kserver_method: 4345102843Speter start_tcp_server (current_parsed_root, &to_server, &from_server); 434625839Speter break; 4347128269Speter#endif /* HAVE_KERBEROS */ 434817721Speter 434981407Speter#ifdef HAVE_GSSAPI 435032785Speter case gserver_method: 435132785Speter /* GSSAPI authentication is handled by the pserver. */ 4352102843Speter connect_to_pserver (current_parsed_root, &to_server, &from_server, 0, 1); 435332785Speter break; 4354128269Speter#endif /* HAVE_GSSAPI */ 435532785Speter 435625839Speter case ext_method: 4357175266Sobrien case extssh_method: 4358128269Speter#ifdef NO_EXT_METHOD 435926801Speter error (0, 0, ":ext: method not supported by this port of CVS"); 436026801Speter error (1, 0, "try :server: instead"); 4361128269Speter#else /* ! NO_EXT_METHOD */ 4362102843Speter start_rsh_server (current_parsed_root, &to_server, &from_server); 4363128269Speter#endif /* NO_EXT_METHOD */ 436425839Speter break; 436525839Speter 436625839Speter case server_method: 4367128269Speter#ifdef START_SERVER 4368102843Speter { 4369102843Speter int tofd, fromfd; 437025839Speter START_SERVER (&tofd, &fromfd, getcaller (), 437181407Speter current_parsed_root->username, current_parsed_root->hostname, 437281407Speter current_parsed_root->directory); 4373102843Speter# ifdef START_SERVER_RETURNS_SOCKET 4374102843Speter make_bufs_from_fds (tofd, fromfd, 0, &to_server, &from_server, 1); 4375128269Speter# else /* ! START_SERVER_RETURNS_SOCKET */ 4376102843Speter make_bufs_from_fds (tofd, fromfd, 0, &to_server, &from_server, 0); 4377102843Speter# endif /* START_SERVER_RETURNS_SOCKET */ 4378102843Speter } 4379128269Speter#else /* ! START_SERVER */ 438025839Speter /* FIXME: It should be possible to implement this portably, 438125839Speter like pserver, which would get rid of the duplicated code 438225839Speter in {vms,windows-NT,...}/startserver.c. */ 4383102843Speter error (1, 0, 4384102843Speter"the :server: access method is not supported by this port of CVS"); 4385128269Speter#endif /* START_SERVER */ 438625839Speter break; 438717721Speter 438854427Speter case fork_method: 4389102843Speter connect_to_forked_server (&to_server, &from_server); 439054427Speter break; 439154427Speter 439225839Speter default: 439325839Speter error (1, 0, "\ 439425839Speter(start_server internal error): unknown access method"); 439525839Speter break; 439625839Speter } 439717721Speter 439817721Speter /* "Hi, I'm Darlene and I'll be your server tonight..." */ 439917721Speter server_started = 1; 440017721Speter 4401102843Speter /* Set up logfiles, if any. 4402102843Speter * 4403102843Speter * We do this _after_ authentication on purpose. Wouldn't really like to 4404102843Speter * worry about logging passwords... 4405102843Speter */ 440625839Speter if (log) 440725839Speter { 440825839Speter int len = strlen (log); 440925839Speter char *buf = xmalloc (len + 5); 441025839Speter char *p; 441125839Speter FILE *fp; 441225839Speter 441325839Speter strcpy (buf, log); 441425839Speter p = buf + len; 441525839Speter 441625839Speter /* Open logfiles in binary mode so that they reflect 441725839Speter exactly what was transmitted and received (that is 441825839Speter more important than that they be maximally 441925839Speter convenient to view). */ 442032785Speter /* Note that if we create several connections in a single CVS client 442132785Speter (currently used by update.c), then the last set of logfiles will 442232785Speter overwrite the others. There is currently no way around this. */ 442325839Speter strcpy (p, ".in"); 442425839Speter fp = open_file (buf, "wb"); 442525839Speter if (fp == NULL) 442625839Speter error (0, errno, "opening to-server logfile %s", buf); 442725839Speter else 442825839Speter to_server = log_buffer_initialize (to_server, fp, 0, 442934461Speter (BUFMEMERRPROC) NULL); 443025839Speter 443125839Speter strcpy (p, ".out"); 443225839Speter fp = open_file (buf, "wb"); 443325839Speter if (fp == NULL) 443425839Speter error (0, errno, "opening from-server logfile %s", buf); 443525839Speter else 443625839Speter from_server = log_buffer_initialize (from_server, fp, 1, 443734461Speter (BUFMEMERRPROC) NULL); 443825839Speter 443925839Speter free (buf); 444025839Speter } 444125839Speter 444217721Speter /* Clear static variables. */ 444317721Speter if (toplevel_repos != NULL) 444425839Speter free (toplevel_repos); 444517721Speter toplevel_repos = NULL; 444617721Speter if (last_repos != NULL) 444725839Speter free (last_repos); 444817721Speter last_repos = NULL; 444917721Speter if (last_update_dir != NULL) 445025839Speter free (last_update_dir); 445117721Speter last_update_dir = NULL; 445217721Speter stored_checksum_valid = 0; 445354427Speter if (stored_mode != NULL) 445454427Speter { 445554427Speter free (stored_mode); 445654427Speter stored_mode = NULL; 445754427Speter } 445817721Speter 4459128269Speter rootless = (strcmp (cvs_cmd_name, "init") == 0); 446066528Speter if (!rootless) 446117721Speter { 446217721Speter send_to_server ("Root ", 0); 446381407Speter send_to_server (current_parsed_root->directory, 0); 446417721Speter send_to_server ("\012", 1); 446517721Speter } 446617721Speter 446717721Speter { 446817721Speter struct response *rs; 446917721Speter 447017721Speter send_to_server ("Valid-responses", 0); 447117721Speter 447217721Speter for (rs = responses; rs->name != NULL; ++rs) 447317721Speter { 447417721Speter send_to_server (" ", 0); 447517721Speter send_to_server (rs->name, 0); 447617721Speter } 447717721Speter send_to_server ("\012", 1); 447817721Speter } 447917721Speter send_to_server ("valid-requests\012", 0); 448017721Speter 448117721Speter if (get_server_responses ()) 448225839Speter error_exit (); 448317721Speter 448417721Speter /* 448517721Speter * Now handle global options. 448617721Speter * 448717721Speter * -H, -f, -d, -e should be handled OK locally. 448817721Speter * 448917721Speter * -b we ignore (treating it as a server installation issue). 449017721Speter * FIXME: should be an error message. 449117721Speter * 449217721Speter * -v we print local version info; FIXME: Add a protocol request to get 449317721Speter * the version from the server so we can print that too. 449417721Speter * 449517721Speter * -l -t -r -w -q -n and -Q need to go to the server. 449617721Speter */ 449717721Speter 449817721Speter { 449917721Speter int have_global = supported_request ("Global_option"); 450017721Speter 450117721Speter if (noexec) 450217721Speter { 450317721Speter if (have_global) 450417721Speter { 450517721Speter send_to_server ("Global_option -n\012", 0); 450617721Speter } 450717721Speter else 450817721Speter error (1, 0, 450917721Speter "This server does not support the global -n option."); 451017721Speter } 451117721Speter if (quiet) 451217721Speter { 451317721Speter if (have_global) 451417721Speter { 451517721Speter send_to_server ("Global_option -q\012", 0); 451617721Speter } 451717721Speter else 451817721Speter error (1, 0, 451917721Speter "This server does not support the global -q option."); 452017721Speter } 452117721Speter if (really_quiet) 452217721Speter { 452317721Speter if (have_global) 452417721Speter { 452517721Speter send_to_server ("Global_option -Q\012", 0); 452617721Speter } 452717721Speter else 452817721Speter error (1, 0, 452917721Speter "This server does not support the global -Q option."); 453017721Speter } 453117721Speter if (!cvswrite) 453217721Speter { 453317721Speter if (have_global) 453417721Speter { 453517721Speter send_to_server ("Global_option -r\012", 0); 453617721Speter } 453717721Speter else 453817721Speter error (1, 0, 453917721Speter "This server does not support the global -r option."); 454017721Speter } 454117721Speter if (trace) 454217721Speter { 454317721Speter if (have_global) 454417721Speter { 454517721Speter send_to_server ("Global_option -t\012", 0); 454617721Speter } 454717721Speter else 454817721Speter error (1, 0, 454917721Speter "This server does not support the global -t option."); 455017721Speter } 455117721Speter } 455232785Speter 455332896Speter /* Find out about server-side cvswrappers. An extra network 455432896Speter turnaround for cvs import seems to be unavoidable, unless we 455532896Speter want to add some kind of client-side place to configure which 455632896Speter filenames imply binary. For cvs add, we could avoid the 455732896Speter problem by keeping a copy of the wrappers in CVSADM (the main 455832896Speter reason to bother would be so we could make add work without 455932896Speter contacting the server, I suspect). */ 456032896Speter 4561128269Speter if ((strcmp (cvs_cmd_name, "import") == 0) 4562128269Speter || (strcmp (cvs_cmd_name, "add") == 0)) 456332896Speter { 456432896Speter if (supported_request ("wrapper-sendme-rcsOptions")) 456532896Speter { 456632896Speter int err; 456732896Speter send_to_server ("wrapper-sendme-rcsOptions\012", 0); 456832896Speter err = get_server_responses (); 456932896Speter if (err != 0) 457032896Speter error (err, 0, "error reading from server"); 457132896Speter } 457232896Speter } 457332896Speter 457466528Speter if (cvsencrypt && !rootless) 457525839Speter { 457625839Speter#ifdef ENCRYPTION 457725839Speter /* Turn on encryption before turning on compression. We do 457825839Speter not want to try to compress the encrypted stream. Instead, 457925839Speter we want to encrypt the compressed stream. If we can't turn 458025839Speter on encryption, bomb out; don't let the user think the data 458125839Speter is being encrypted when it is not. */ 458225839Speter#ifdef HAVE_KERBEROS 458381407Speter if (current_parsed_root->method == kserver_method) 458425839Speter { 458525839Speter if (! supported_request ("Kerberos-encrypt")) 458625839Speter error (1, 0, "This server does not support encryption"); 458725839Speter send_to_server ("Kerberos-encrypt\012", 0); 458825839Speter to_server = krb_encrypt_buffer_initialize (to_server, 0, sched, 458925839Speter kblock, 459034461Speter (BUFMEMERRPROC) NULL); 459125839Speter from_server = krb_encrypt_buffer_initialize (from_server, 1, 459225839Speter sched, kblock, 459334461Speter (BUFMEMERRPROC) NULL); 459425839Speter } 459525839Speter else 459625839Speter#endif /* HAVE_KERBEROS */ 459732785Speter#ifdef HAVE_GSSAPI 459881407Speter if (current_parsed_root->method == gserver_method) 459932785Speter { 460032785Speter if (! supported_request ("Gssapi-encrypt")) 460132785Speter error (1, 0, "This server does not support encryption"); 460232785Speter send_to_server ("Gssapi-encrypt\012", 0); 460332785Speter to_server = cvs_gssapi_wrap_buffer_initialize (to_server, 0, 460432785Speter gcontext, 460534461Speter ((BUFMEMERRPROC) 460634461Speter NULL)); 460732785Speter from_server = cvs_gssapi_wrap_buffer_initialize (from_server, 1, 460832785Speter gcontext, 460934461Speter ((BUFMEMERRPROC) 461034461Speter NULL)); 461132785Speter cvs_gssapi_encrypt = 1; 461232785Speter } 461332785Speter else 461432785Speter#endif /* HAVE_GSSAPI */ 461532785Speter error (1, 0, "Encryption is only supported when using GSSAPI or Kerberos"); 461625839Speter#else /* ! ENCRYPTION */ 461725839Speter error (1, 0, "This client does not support encryption"); 461825839Speter#endif /* ! ENCRYPTION */ 461925839Speter } 462032785Speter 462166528Speter if (gzip_level && !rootless) 462217721Speter { 462325839Speter if (supported_request ("Gzip-stream")) 462417721Speter { 462525839Speter char gzip_level_buf[5]; 462625839Speter send_to_server ("Gzip-stream ", 0); 462725839Speter sprintf (gzip_level_buf, "%d", gzip_level); 462825839Speter send_to_server (gzip_level_buf, 0); 462925839Speter send_to_server ("\012", 1); 463025839Speter 463125839Speter /* All further communication with the server will be 463225839Speter compressed. */ 463325839Speter 463425839Speter to_server = compress_buffer_initialize (to_server, 0, gzip_level, 463534461Speter (BUFMEMERRPROC) NULL); 463625839Speter from_server = compress_buffer_initialize (from_server, 1, 463725839Speter gzip_level, 463834461Speter (BUFMEMERRPROC) NULL); 463925839Speter } 464025839Speter#ifndef NO_CLIENT_GZIP_PROCESS 464125839Speter else if (supported_request ("gzip-file-contents")) 464225839Speter { 464317721Speter char gzip_level_buf[5]; 464417721Speter send_to_server ("gzip-file-contents ", 0); 464517721Speter sprintf (gzip_level_buf, "%d", gzip_level); 464617721Speter send_to_server (gzip_level_buf, 0); 464717721Speter 464817721Speter send_to_server ("\012", 1); 464925839Speter 465025839Speter file_gzip_level = gzip_level; 465117721Speter } 465225839Speter#endif 465317721Speter else 465417721Speter { 465517721Speter fprintf (stderr, "server doesn't support gzip-file-contents\n"); 465625839Speter /* Setting gzip_level to 0 prevents us from giving the 465725839Speter error twice if update has to contact the server again 465825839Speter to fetch unpatchable files. */ 465917721Speter gzip_level = 0; 466017721Speter } 466117721Speter } 466217721Speter 466366528Speter if (cvsauthenticate && ! cvsencrypt && !rootless) 466432785Speter { 466532785Speter /* Turn on authentication after turning on compression, so 466632785Speter that we can compress the authentication information. We 466732785Speter assume that encrypted data is always authenticated--the 466832785Speter ability to decrypt the data stream is itself a form of 466932785Speter authentication. */ 467032785Speter#ifdef HAVE_GSSAPI 467181407Speter if (current_parsed_root->method == gserver_method) 467232785Speter { 467332785Speter if (! supported_request ("Gssapi-authenticate")) 467432785Speter error (1, 0, 467532785Speter "This server does not support stream authentication"); 467632785Speter send_to_server ("Gssapi-authenticate\012", 0); 467732785Speter to_server = cvs_gssapi_wrap_buffer_initialize (to_server, 0, 467832785Speter gcontext, 467934461Speter ((BUFMEMERRPROC) 468034461Speter NULL)); 468132785Speter from_server = cvs_gssapi_wrap_buffer_initialize (from_server, 1, 468232785Speter gcontext, 468334461Speter ((BUFMEMERRPROC) 468434461Speter NULL)); 468532785Speter } 468632785Speter else 468732785Speter error (1, 0, "Stream authentication is only supported when using GSSAPI"); 468832785Speter#else /* ! HAVE_GSSAPI */ 468932785Speter error (1, 0, "This client does not support stream authentication"); 469032785Speter#endif /* ! HAVE_GSSAPI */ 469132785Speter } 469232785Speter 469317721Speter /* If "Set" is not supported, just silently fail to send the variables. 469417721Speter Users with an old server should get a useful error message when it 469517721Speter fails to recognize the ${=foo} syntax. This way if someone uses 469617721Speter several servers, some of which are new and some old, they can still 469717721Speter set user variables in their .cvsrc without trouble. */ 469817721Speter if (supported_request ("Set")) 469917721Speter walklist (variable_list, send_variable_proc, NULL); 470017721Speter} 470117721Speter 4702128269Speter 4703128269Speter 470426801Speter#ifndef NO_EXT_METHOD 470526801Speter 470617721Speter/* Contact the server by starting it with rsh. */ 470717721Speter 470817721Speter/* Right now, we have two different definitions for this function, 470917721Speter depending on whether we start the rsh server using popenRW or not. 471017721Speter This isn't ideal, and the best thing would probably be to change 471117721Speter the OS/2 port to be more like the regular Unix client (i.e., by 471217721Speter implementing piped_child)... but I'm doing something else at the 471317721Speter moment, and wish to make only one change at a time. -Karl */ 471417721Speter 4715102843Speter# ifdef START_RSH_WITH_POPEN_RW 471617721Speter 471717721Speter/* This is actually a crock -- it's OS/2-specific, for no one else 471817721Speter uses it. If I get time, I want to make piped_child and all the 471917721Speter other stuff in os2/run.c work right. In the meantime, this gets us 472017721Speter up and running, and that's most important. */ 472117721Speter 472217721Speterstatic void 4723102843Speterstart_rsh_server (root, to_server, from_server) 4724102843Speter cvsroot_t *root; 4725102843Speter struct buffer **to_server; 4726102843Speter struct buffer **from_server; 472717721Speter{ 472825839Speter int pipes[2]; 4729102843Speter int child_pid; 473025839Speter 473125839Speter /* If you're working through firewalls, you can set the 473225839Speter CVS_RSH environment variable to a script which uses rsh to 473325839Speter invoke another rsh on a proxy machine. */ 4734177394Sobrien char *env_cvs_rsh = getenv ("CVS_RSH"); 4735177394Sobrien char *env_cvs_ssh = getenv ("CVS_SSH"); 4736177394Sobrien char *cvs_rsh; 473725839Speter char *cvs_server = getenv ("CVS_SERVER"); 473825839Speter int i = 0; 473925839Speter /* This needs to fit "rsh", "-b", "-l", "USER", "host", 474025839Speter "cmd (w/ args)", and NULL. We leave some room to grow. */ 474125839Speter char *rsh_argv[10]; 474225839Speter 4743177394Sobrien if (root->method == extssh_method) 4744177394Sobrien cvs_rsh = env_cvs_ssh ? env_cvs_ssh : SSH_DFLT; 4745177394Sobrien else 4746177394Sobrien cvs_rsh = env_cvs_rsh ? env_cvs_rsh : RSH_DFLT; 4747177394Sobrien 474825839Speter if (!cvs_server) 474925839Speter cvs_server = "cvs"; 475025839Speter 475125839Speter /* The command line starts out with rsh. */ 475225839Speter rsh_argv[i++] = cvs_rsh; 475325839Speter 4754102843Speter# ifdef RSH_NEEDS_BINARY_FLAG 475525839Speter /* "-b" for binary, under OS/2. */ 475625839Speter rsh_argv[i++] = "-b"; 4757102843Speter# endif /* RSH_NEEDS_BINARY_FLAG */ 475817721Speter 475925839Speter /* Then we strcat more things on the end one by one. */ 4760102843Speter if (root->username != NULL) 476117721Speter { 476225839Speter rsh_argv[i++] = "-l"; 4763102843Speter rsh_argv[i++] = root->username; 476417721Speter } 476517721Speter 4766102843Speter rsh_argv[i++] = root->hostname; 476725839Speter rsh_argv[i++] = cvs_server; 476825839Speter rsh_argv[i++] = "server"; 476917721Speter 477025839Speter /* Mark the end of the arg list. */ 477125839Speter rsh_argv[i] = (char *) NULL; 477225839Speter 477325839Speter if (trace) 477417721Speter { 477525839Speter fprintf (stderr, " -> Starting server: "); 477681407Speter for (i = 0; rsh_argv[i]; i++) 477781407Speter fprintf (stderr, "%s ", rsh_argv[i]); 477825839Speter putc ('\n', stderr); 477917721Speter } 478017721Speter 478125839Speter /* Do the deed. */ 4782102843Speter child_pid = popenRW (rsh_argv, pipes); 4783102843Speter if (child_pid < 0) 478425839Speter error (1, errno, "cannot start server via rsh"); 478525839Speter 4786102843Speter /* Give caller the file descriptors in a form it can deal with. */ 4787102843Speter make_bufs_from_fds (pipes[0], pipes[1], child_pid, to_server, from_server, 0); 478817721Speter} 478917721Speter 4790102843Speter# else /* ! START_RSH_WITH_POPEN_RW */ 479117721Speter 479217721Speterstatic void 4793102843Speterstart_rsh_server (root, to_server, from_server) 4794102843Speter cvsroot_t *root; 4795102843Speter struct buffer **to_server; 4796102843Speter struct buffer **from_server; 479717721Speter{ 479817721Speter /* If you're working through firewalls, you can set the 479917721Speter CVS_RSH environment variable to a script which uses rsh to 480017721Speter invoke another rsh on a proxy machine. */ 4801177394Sobrien char *env_cvs_rsh = getenv ("CVS_RSH"); 4802177394Sobrien char *env_cvs_ssh = getenv ("CVS_SSH"); 4803177394Sobrien char *cvs_rsh; 480417721Speter char *cvs_server = getenv ("CVS_SERVER"); 480517721Speter char *command; 4806102843Speter int tofd, fromfd; 4807102843Speter int child_pid; 480817721Speter 4809177394Sobrien if (root->method == extssh_method) 4810177394Sobrien cvs_rsh = env_cvs_ssh ? env_cvs_ssh : SSH_DFLT; 4811177394Sobrien else 4812177394Sobrien cvs_rsh = env_cvs_rsh ? env_cvs_rsh : RSH_DFLT; 4813177394Sobrien 481417721Speter if (!cvs_server) 481517721Speter cvs_server = "cvs"; 481617721Speter 481717721Speter /* Pass the command to rsh as a single string. This shouldn't 481817721Speter affect most rsh servers at all, and will pacify some buggy 481917721Speter versions of rsh that grab switches out of the middle of the 482017721Speter command (they're calling the GNU getopt routines incorrectly). */ 4821102843Speter command = xmalloc (strlen (cvs_server) + 8); 482217721Speter 482317721Speter /* If you are running a very old (Nov 3, 1994, before 1.5) 482417721Speter * version of the server, you need to make sure that your .bashrc 482517721Speter * on the server machine does not set CVSROOT to something 482617721Speter * containing a colon (or better yet, upgrade the server). */ 482717721Speter sprintf (command, "%s server", cvs_server); 482817721Speter 482917721Speter { 4830128269Speter const char *argv[10]; 4831128269Speter const char **p = argv; 483217721Speter 483317721Speter *p++ = cvs_rsh; 483417721Speter 483517721Speter /* If the login names differ between client and server 483617721Speter * pass it on to rsh. 483717721Speter */ 4838102843Speter if (root->username != NULL) 483917721Speter { 484017721Speter *p++ = "-l"; 4841102843Speter *p++ = root->username; 484217721Speter } 484317721Speter 4844130307Speter *p++ = root->hostname; 484517721Speter *p++ = command; 484617721Speter *p++ = NULL; 484717721Speter 484817721Speter if (trace) 484917721Speter { 485017721Speter int i; 485117721Speter 485217721Speter fprintf (stderr, " -> Starting server: "); 485317721Speter for (i = 0; argv[i]; i++) 485417721Speter fprintf (stderr, "%s ", argv[i]); 485517721Speter putc ('\n', stderr); 485617721Speter } 4857175266Sobrien child_pid = piped_child (argv, &tofd, &fromfd, 1); 485817721Speter 4859102843Speter if (child_pid < 0) 486017721Speter error (1, errno, "cannot start server via rsh"); 486117721Speter } 486225839Speter free (command); 4863102843Speter 4864102843Speter make_bufs_from_fds (tofd, fromfd, child_pid, to_server, from_server, 0); 486517721Speter} 486617721Speter 4867102843Speter# endif /* START_RSH_WITH_POPEN_RW */ 486817721Speter 486926801Speter#endif /* NO_EXT_METHOD */ 487026801Speter 487117721Speter 4872102843Speter 487317721Speter/* Send an argument STRING. */ 487417721Spetervoid 487517721Spetersend_arg (string) 4876175266Sobrien const char *string; 487717721Speter{ 487817721Speter char buf[1]; 4879175266Sobrien const char *p = string; 488017721Speter 488117721Speter send_to_server ("Argument ", 0); 488217721Speter 488317721Speter while (*p) 488417721Speter { 488517721Speter if (*p == '\n') 488617721Speter { 488717721Speter send_to_server ("\012Argumentx ", 0); 488817721Speter } 488917721Speter else 489017721Speter { 489117721Speter buf[0] = *p; 489217721Speter send_to_server (buf, 1); 489317721Speter } 489417721Speter ++p; 489517721Speter } 489617721Speter send_to_server ("\012", 1); 489717721Speter} 489817721Speter 4899128269Speter 4900128269Speter 4901128269Speterstatic void send_modified PROTO ((const char *, const char *, Vers_TS *)); 4902128269Speter 490325839Speter/* VERS->OPTIONS specifies whether the file is binary or not. NOTE: BEFORE 490425839Speter using any other fields of the struct vers, we would need to fix 490525839Speter client_process_import_file to set them up. */ 490625839Speter 490717721Speterstatic void 490817721Spetersend_modified (file, short_pathname, vers) 4909128269Speter const char *file; 4910128269Speter const char *short_pathname; 491117721Speter Vers_TS *vers; 491217721Speter{ 491317721Speter /* File was modified, send it. */ 491417721Speter struct stat sb; 491517721Speter int fd; 491617721Speter char *buf; 491717721Speter char *mode_string; 491832785Speter size_t bufsize; 491917721Speter int bin; 492017721Speter 492125839Speter if (trace) 492225839Speter (void) fprintf (stderr, " -> Sending file `%s' to server\n", file); 492325839Speter 492417721Speter /* Don't think we can assume fstat exists. */ 492525839Speter if ( CVS_STAT (file, &sb) < 0) 492617721Speter error (1, errno, "reading %s", short_pathname); 492717721Speter 492817721Speter mode_string = mode_to_string (sb.st_mode); 492917721Speter 493017721Speter /* Beware: on systems using CRLF line termination conventions, 493117721Speter the read and write functions will convert CRLF to LF, so the 493217721Speter number of characters read is not the same as sb.st_size. Text 493317721Speter files should always be transmitted using the LF convention, so 493417721Speter we don't want to disable this conversion. */ 493517721Speter bufsize = sb.st_size; 493617721Speter buf = xmalloc (bufsize); 493717721Speter 493817721Speter /* Is the file marked as containing binary data by the "-kb" flag? 493917721Speter If so, make sure to open it in binary mode: */ 494017721Speter 494117721Speter if (vers && vers->options) 494217721Speter bin = !(strcmp (vers->options, "-kb")); 494317721Speter else 494417721Speter bin = 0; 494517721Speter 494625839Speter#ifdef BROKEN_READWRITE_CONVERSION 494725839Speter if (!bin) 494825839Speter { 494925839Speter /* If only stdio, not open/write/etc., do text/binary 495025839Speter conversion, use convert_file which can compensate 495125839Speter (FIXME: we could just use stdio instead which would 495225839Speter avoid the whole problem). */ 495325839Speter char tfile[1024]; strcpy(tfile, file); strcat(tfile, ".CVSBFCTMP"); 495425839Speter convert_file (file, O_RDONLY, 495525839Speter tfile, O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY); 495625839Speter fd = CVS_OPEN (tfile, O_RDONLY | OPEN_BINARY); 495725839Speter if (fd < 0) 495825839Speter error (1, errno, "reading %s", short_pathname); 495925839Speter } 496025839Speter else 496125839Speter fd = CVS_OPEN (file, O_RDONLY | OPEN_BINARY); 496225839Speter#else 496325839Speter fd = CVS_OPEN (file, O_RDONLY | (bin ? OPEN_BINARY : 0)); 496425839Speter#endif 496517721Speter 496617721Speter if (fd < 0) 496717721Speter error (1, errno, "reading %s", short_pathname); 496817721Speter 496925839Speter if (file_gzip_level && sb.st_size > 100) 497017721Speter { 497132785Speter size_t newsize = 0; 497217721Speter 497354427Speter if (read_and_gzip (fd, short_pathname, (unsigned char **)&buf, 497454427Speter &bufsize, &newsize, 497554427Speter file_gzip_level)) 497654427Speter error (1, 0, "aborting due to compression error"); 497717721Speter 497817721Speter if (close (fd) < 0) 497917721Speter error (0, errno, "warning: can't close %s", short_pathname); 498017721Speter 498117721Speter { 498217721Speter char tmp[80]; 498317721Speter 498417721Speter send_to_server ("Modified ", 0); 498517721Speter send_to_server (file, 0); 498617721Speter send_to_server ("\012", 1); 498717721Speter send_to_server (mode_string, 0); 498817721Speter send_to_server ("\012z", 2); 498917721Speter sprintf (tmp, "%lu\n", (unsigned long) newsize); 499017721Speter send_to_server (tmp, 0); 499117721Speter 499217721Speter send_to_server (buf, newsize); 499317721Speter } 499417721Speter } 499517721Speter else 499617721Speter { 499717721Speter int newsize; 499817721Speter 499917721Speter { 500017721Speter char *bufp = buf; 500117721Speter int len; 500217721Speter 500317721Speter /* FIXME: This is gross. It assumes that we might read 500417721Speter less than st_size bytes (true on NT), but not more. 500517721Speter Instead of this we should just be reading a block of 500617721Speter data (e.g. 8192 bytes), writing it to the network, and 500717721Speter so on until EOF. */ 500817721Speter while ((len = read (fd, bufp, (buf + sb.st_size) - bufp)) > 0) 500917721Speter bufp += len; 501017721Speter 501117721Speter if (len < 0) 501217721Speter error (1, errno, "reading %s", short_pathname); 501317721Speter 501417721Speter newsize = bufp - buf; 501517721Speter } 501617721Speter if (close (fd) < 0) 501717721Speter error (0, errno, "warning: can't close %s", short_pathname); 501817721Speter 501917721Speter { 502017721Speter char tmp[80]; 502117721Speter 502217721Speter send_to_server ("Modified ", 0); 502317721Speter send_to_server (file, 0); 502417721Speter send_to_server ("\012", 1); 502517721Speter send_to_server (mode_string, 0); 502617721Speter send_to_server ("\012", 1); 502717721Speter sprintf (tmp, "%lu\012", (unsigned long) newsize); 502817721Speter send_to_server (tmp, 0); 502917721Speter } 503025839Speter#ifdef BROKEN_READWRITE_CONVERSION 503125839Speter if (!bin) 503225839Speter { 503325839Speter char tfile[1024]; strcpy(tfile, file); strcat(tfile, ".CVSBFCTMP"); 503425839Speter if (CVS_UNLINK (tfile) < 0) 503525839Speter error (0, errno, "warning: can't remove temp file %s", tfile); 503625839Speter } 503725839Speter#endif 503817721Speter 503917721Speter /* 504017721Speter * Note that this only ends with a newline if the file ended with 504117721Speter * one. 504217721Speter */ 504317721Speter if (newsize > 0) 504425839Speter send_to_server (buf, newsize); 504517721Speter } 504617721Speter free (buf); 504717721Speter free (mode_string); 504817721Speter} 504917721Speter 505025839Speter/* The address of an instance of this structure is passed to 505125839Speter send_fileproc, send_filesdoneproc, and send_direntproc, as the 505225839Speter callerdat parameter. */ 505317721Speter 505425839Speterstruct send_data 505525839Speter{ 505625839Speter /* Each of the following flags are zero for clear or nonzero for set. */ 505725839Speter int build_dirs; 505825839Speter int force; 505925839Speter int no_contents; 506066528Speter int backup_modified; 506125839Speter}; 506225839Speter 506325839Speterstatic int send_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 506425839Speter 506517721Speter/* Deal with one file. */ 506617721Speterstatic int 506725839Spetersend_fileproc (callerdat, finfo) 506825839Speter void *callerdat; 506917721Speter struct file_info *finfo; 507017721Speter{ 507125839Speter struct send_data *args = (struct send_data *) callerdat; 507217721Speter Vers_TS *vers; 507325839Speter struct file_info xfinfo; 507425839Speter /* File name to actually use. Might differ in case from 507525839Speter finfo->file. */ 5076128269Speter const char *filename; 507717721Speter 507817721Speter send_a_repository ("", finfo->repository, finfo->update_dir); 507917721Speter 508025839Speter xfinfo = *finfo; 508125839Speter xfinfo.repository = NULL; 508225839Speter xfinfo.rcs = NULL; 508325839Speter vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0); 508417721Speter 508525839Speter if (vers->entdata != NULL) 508625839Speter filename = vers->entdata->user; 508725839Speter else 508825839Speter filename = finfo->file; 508925839Speter 509017721Speter if (vers->vn_user != NULL) 509117721Speter { 509217721Speter /* The Entries request. */ 509332785Speter send_to_server ("Entry /", 0); 509432785Speter send_to_server (filename, 0); 509532785Speter send_to_server ("/", 0); 509632785Speter send_to_server (vers->vn_user, 0); 509732785Speter send_to_server ("/", 0); 509832785Speter if (vers->ts_conflict != NULL) 509932785Speter { 510032785Speter if (vers->ts_user != NULL && 510132785Speter strcmp (vers->ts_conflict, vers->ts_user) == 0) 510232785Speter send_to_server ("+=", 0); 510332785Speter else 510432785Speter send_to_server ("+modified", 0); 510532785Speter } 510632785Speter send_to_server ("/", 0); 510732785Speter send_to_server (vers->entdata != NULL 510832785Speter ? vers->entdata->options 510932785Speter : vers->options, 511032785Speter 0); 511132785Speter send_to_server ("/", 0); 511217721Speter if (vers->entdata != NULL && vers->entdata->tag) 511317721Speter { 511417721Speter send_to_server ("T", 0); 511517721Speter send_to_server (vers->entdata->tag, 0); 511617721Speter } 511717721Speter else if (vers->entdata != NULL && vers->entdata->date) 511817721Speter { 511917721Speter send_to_server ("D", 0); 512017721Speter send_to_server (vers->entdata->date, 0); 512117721Speter } 512217721Speter send_to_server ("\012", 1); 512317721Speter } 512432785Speter else 512532785Speter { 512632785Speter /* It seems a little silly to re-read this on each file, but 512732785Speter send_dirent_proc doesn't get called if filenames are specified 512832785Speter explicitly on the command line. */ 512932785Speter wrap_add_file (CVSDOTWRAPPER, 1); 513017721Speter 513132785Speter if (wrap_name_has (filename, WRAP_RCSOPTION)) 513232785Speter { 513332785Speter /* No "Entry", but the wrappers did give us a kopt so we better 513432785Speter send it with "Kopt". As far as I know this only happens 513532785Speter for "cvs add". Question: is there any reason why checking 513632785Speter for options from wrappers isn't done in Version_TS? 513732785Speter 513832785Speter Note: it might have been better to just remember all the 513932785Speter kopts on the client side, rather than send them to the server, 514032785Speter and have it send us back the same kopts. But that seemed like 514132785Speter a bigger change than I had in mind making now. */ 514232785Speter 514332785Speter if (supported_request ("Kopt")) 514432785Speter { 514532785Speter char *opt; 514632785Speter 514732785Speter send_to_server ("Kopt ", 0); 514832785Speter opt = wrap_rcsoption (filename, 1); 514932785Speter send_to_server (opt, 0); 515032785Speter send_to_server ("\012", 1); 515132785Speter free (opt); 515232785Speter } 515332785Speter else 515432785Speter error (0, 0, 515532785Speter "\ 515632785Speterwarning: ignoring -k options due to server limitations"); 515732785Speter } 515832785Speter } 515932785Speter 516017721Speter if (vers->ts_user == NULL) 516117721Speter { 516217721Speter /* 516317721Speter * Do we want to print "file was lost" like normal CVS? 516417721Speter * Would it always be appropriate? 516517721Speter */ 516625839Speter /* File no longer exists. Don't do anything, missing files 516725839Speter just happen. */ 516817721Speter } 516917721Speter else if (vers->ts_rcs == NULL 517025839Speter || args->force 5171175266Sobrien || strcmp (vers->ts_conflict 5172175266Sobrien && supported_request ("Empty-conflicts") 5173175266Sobrien ? vers->ts_conflict : vers->ts_rcs, vers->ts_user) 5174175266Sobrien || (vers->ts_conflict && !strcmp (cvs_cmd_name, "diff")) 517599583Sfenner || (vers->vn_user && *vers->vn_user == '0')) 517617721Speter { 517725839Speter if (args->no_contents 517825839Speter && supported_request ("Is-modified")) 517925839Speter { 518025839Speter send_to_server ("Is-modified ", 0); 518125839Speter send_to_server (filename, 0); 518225839Speter send_to_server ("\012", 1); 518325839Speter } 518425839Speter else 518525839Speter send_modified (filename, finfo->fullname, vers); 518666528Speter 518766528Speter if (args->backup_modified) 518866528Speter { 518966528Speter char *bakname; 519066528Speter bakname = backup_file (filename, vers->vn_user); 519166528Speter /* This behavior is sufficiently unexpected to 519266528Speter justify overinformativeness, I think. */ 519366528Speter if (! really_quiet) 519466528Speter printf ("(Locally modified %s moved to %s)\n", 519566528Speter filename, bakname); 519666528Speter free (bakname); 519766528Speter } 519817721Speter } 519917721Speter else 520017721Speter { 520125839Speter send_to_server ("Unchanged ", 0); 520225839Speter send_to_server (filename, 0); 520325839Speter send_to_server ("\012", 1); 520417721Speter } 520517721Speter 520617721Speter /* if this directory has an ignore list, add this file to it */ 520717721Speter if (ignlist) 520817721Speter { 520917721Speter Node *p; 521017721Speter 521117721Speter p = getnode (); 521217721Speter p->type = FILES; 521317721Speter p->key = xstrdup (finfo->file); 521417721Speter (void) addnode (ignlist, p); 521517721Speter } 521617721Speter 521717721Speter freevers_ts (&vers); 521817721Speter return 0; 521917721Speter} 522017721Speter 522117721Speter 5222128269Speter 5223128269Speterstatic void send_ignproc PROTO ((const char *, const char *)); 5224128269Speter 522517721Speterstatic void 522617721Spetersend_ignproc (file, dir) 5227128269Speter const char *file; 5228128269Speter const char *dir; 522917721Speter{ 523017721Speter if (ign_inhibit_server || !supported_request ("Questionable")) 523117721Speter { 523217721Speter if (dir[0] != '\0') 523317721Speter (void) printf ("? %s/%s\n", dir, file); 523417721Speter else 523517721Speter (void) printf ("? %s\n", file); 523617721Speter } 523717721Speter else 523817721Speter { 523917721Speter send_to_server ("Questionable ", 0); 524017721Speter send_to_server (file, 0); 524117721Speter send_to_server ("\012", 1); 524217721Speter } 524317721Speter} 524417721Speter 524517721Speter 5246128269Speter 5247128269Speterstatic int send_filesdoneproc PROTO ((void *, int, const char *, const char *, 5248128269Speter List *)); 5249128269Speter 525017721Speterstatic int 525125839Spetersend_filesdoneproc (callerdat, err, repository, update_dir, entries) 525225839Speter void *callerdat; 525317721Speter int err; 5254128269Speter const char *repository; 5255128269Speter const char *update_dir; 525625839Speter List *entries; 525717721Speter{ 525817721Speter /* if this directory has an ignore list, process it then free it */ 525917721Speter if (ignlist) 526017721Speter { 526125839Speter ignore_files (ignlist, entries, update_dir, send_ignproc); 526217721Speter dellist (&ignlist); 526317721Speter } 526417721Speter 526517721Speter return (err); 526617721Speter} 526717721Speter 5268128269Speterstatic Dtype send_dirent_proc PROTO ((void *, const char *, const char *, 5269128269Speter const char *, List *)); 527017721Speter 527117721Speter/* 527217721Speter * send_dirent_proc () is called back by the recursion processor before a 527317721Speter * sub-directory is processed for update. 527417721Speter * A return code of 0 indicates the directory should be 527517721Speter * processed by the recursion code. A return of non-zero indicates the 527617721Speter * recursion code should skip this directory. 527717721Speter * 527817721Speter */ 527917721Speterstatic Dtype 528025839Spetersend_dirent_proc (callerdat, dir, repository, update_dir, entries) 528125839Speter void *callerdat; 5282128269Speter const char *dir; 5283128269Speter const char *repository; 5284128269Speter const char *update_dir; 528525839Speter List *entries; 528617721Speter{ 528725839Speter struct send_data *args = (struct send_data *) callerdat; 528817721Speter int dir_exists; 528925839Speter char *cvsadm_name; 529017721Speter 529117721Speter if (ignore_directory (update_dir)) 529217721Speter { 529317721Speter /* print the warm fuzzy message */ 529417721Speter if (!quiet) 529517721Speter error (0, 0, "Ignoring %s", update_dir); 529617721Speter return (R_SKIP_ALL); 529717721Speter } 529817721Speter 529925839Speter /* 530025839Speter * If the directory does not exist yet (e.g. "cvs update -d foo"), 530125839Speter * no need to send any files from it. If the directory does not 530225839Speter * have a CVS directory, then we pretend that it does not exist. 530325839Speter * Otherwise, we will fail when trying to open the Entries file. 530425839Speter * This case will happen when checking out a module defined as 530525839Speter * ``-a .''. 530625839Speter */ 530725839Speter cvsadm_name = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); 530825839Speter sprintf (cvsadm_name, "%s/%s", dir, CVSADM); 530925839Speter dir_exists = isdir (cvsadm_name); 531025839Speter free (cvsadm_name); 531125839Speter 531217721Speter /* 531317721Speter * If there is an empty directory (e.g. we are doing `cvs add' on a 531417721Speter * newly-created directory), the server still needs to know about it. 531517721Speter */ 531617721Speter 531732785Speter if (dir_exists) 531817721Speter { 531917721Speter /* 532017721Speter * Get the repository from a CVS/Repository file whenever possible. 532117721Speter * The repository variable is wrong if the names in the local 532217721Speter * directory don't match the names in the repository. 532317721Speter */ 532417721Speter char *repos = Name_Repository (dir, update_dir); 532517721Speter send_a_repository (dir, repos, update_dir); 532617721Speter free (repos); 532766528Speter 532866528Speter /* initialize the ignore list for this directory */ 532966528Speter ignlist = getlist (); 533017721Speter } 533117721Speter else 533225839Speter { 533332785Speter /* It doesn't make sense to send a non-existent directory, 533432785Speter because there is no way to get the correct value for 533532785Speter the repository (I suppose maybe via the expand-modules 533632785Speter request). In the case where the "obvious" choice for 533732785Speter repository is correct, the server can figure out whether 533832785Speter to recreate the directory; in the case where it is wrong 533932785Speter (that is, does not match what modules give us), we might as 534032785Speter well just fail to recreate it. 534132785Speter 534232785Speter Checking for noexec is a kludge for "cvs -n add dir". */ 534325839Speter /* Don't send a non-existent directory unless we are building 534425839Speter new directories (build_dirs is true). Otherwise, CVS may 534525839Speter see a D line in an Entries file, and recreate a directory 534625839Speter which the user removed by hand. */ 534732785Speter if (args->build_dirs && noexec) 534825839Speter send_a_repository (dir, repository, update_dir); 534925839Speter } 535017721Speter 535117721Speter return (dir_exists ? R_PROCESS : R_SKIP_ALL); 535217721Speter} 535317721Speter 535466528Speter 5355128269Speter 5356128269Speterstatic int send_dirleave_proc PROTO ((void *, const char *, int, const char *, 5357128269Speter List *)); 5358128269Speter 535917721Speter/* 536066528Speter * send_dirleave_proc () is called back by the recursion code upon leaving 536166528Speter * a directory. All it does is delete the ignore list if it hasn't already 536266528Speter * been done (by send_filesdone_proc). 536366528Speter */ 536466528Speter/* ARGSUSED */ 536566528Speterstatic int 536666528Spetersend_dirleave_proc (callerdat, dir, err, update_dir, entries) 536766528Speter void *callerdat; 5368128269Speter const char *dir; 536966528Speter int err; 5370128269Speter const char *update_dir; 537166528Speter List *entries; 537266528Speter{ 537366528Speter 537466528Speter /* Delete the ignore list if it hasn't already been done. */ 537566528Speter if (ignlist) 537666528Speter dellist (&ignlist); 537766528Speter return err; 537866528Speter} 537966528Speter 538066528Speter/* 5381175266Sobrien * Send each option in an array to the server, one by one. 5382175266Sobrien * argv might be "--foo=bar", "-C", "5", "-y". 538317721Speter */ 538417721Spetervoid 5385175266Sobriensend_options (int argc, char *const *argv) 538617721Speter{ 5387175266Sobrien int i; 5388175266Sobrien for (i = 0; i < argc; i++) 5389175266Sobrien send_arg (argv[i]); 539017721Speter} 539117721Speter 539217721Speter 5393128269Speter 539417721Speter/* Send the names of all the argument files to the server. */ 539517721Spetervoid 539617721Spetersend_file_names (argc, argv, flags) 539717721Speter int argc; 539817721Speter char **argv; 539917721Speter unsigned int flags; 540017721Speter{ 540117721Speter int i; 540254427Speter 540317721Speter /* The fact that we do this here as well as start_recursion is a bit 540417721Speter of a performance hit. Perhaps worth cleaning up someday. */ 540517721Speter if (flags & SEND_EXPAND_WILD) 540617721Speter expand_wild (argc, argv, &argc, &argv); 540717721Speter 540817721Speter for (i = 0; i < argc; ++i) 540917721Speter { 541017721Speter char buf[1]; 5411128269Speter char *p; 5412128269Speter#ifdef FILENAMES_CASE_INSENSITIVE 5413128269Speter char *line = xmalloc (1); 5414128269Speter *line = '\0'; 5415128269Speter#endif /* FILENAMES_CASE_INSENSITIVE */ 541617721Speter 541754427Speter if (arg_should_not_be_sent_to_server (argv[i])) 541854427Speter continue; 541954427Speter 542025839Speter#ifdef FILENAMES_CASE_INSENSITIVE 5421128269Speter /* We want to send the path as it appears in the 5422128269Speter CVS/Entries files. We put this inside an ifdef 542325839Speter to avoid doing all these system calls in 542425839Speter cases where fncmp is just strcmp anyway. */ 5425128269Speter /* The isdir (CVSADM) check could more gracefully be replaced 542625839Speter with a way of having Entries_Open report back the 542725839Speter error to us and letting us ignore existence_error. 542825839Speter Or some such. */ 542925839Speter { 5430128269Speter List *stack; 5431128269Speter size_t line_len = 0; 5432128269Speter char *q, *r; 5433128269Speter struct saved_cwd sdir; 543425839Speter 5435128269Speter /* Split the argument onto the stack. */ 5436128269Speter stack = getlist(); 5437128269Speter r = xstrdup (argv[i]); 5438128269Speter /* It's okay to discard the const from the last_component return 5439128269Speter * below since we know we passed in an arg that was not const. 5440128269Speter */ 5441128269Speter while ((q = (char *)last_component (r)) != r) 544225839Speter { 5443128269Speter push (stack, xstrdup (q)); 5444128269Speter *--q = '\0'; 544525839Speter } 5446128269Speter push (stack, r); 5447128269Speter 5448128269Speter /* Normalize the path into outstr. */ 5449128269Speter save_cwd (&sdir); 5450128269Speter while (q = pop (stack)) 5451128269Speter { 5452128269Speter Node *node = NULL; 5453128269Speter if (isdir (CVSADM)) 5454128269Speter { 5455128269Speter List *entries; 5456128269Speter 5457128269Speter /* Note that if we are adding a directory, 5458128269Speter the following will read the entry 5459128269Speter that we just wrote there, that is, we 5460128269Speter will get the case specified on the 5461128269Speter command line, not the case of the 5462128269Speter directory in the filesystem. This 5463128269Speter is correct behavior. */ 5464128269Speter entries = Entries_Open (0, NULL); 5465128269Speter node = findnode_fn (entries, q); 5466128269Speter if (node != NULL) 5467128269Speter { 5468128269Speter /* Add the slash unless this is our first element. */ 5469128269Speter if (line_len) 5470128269Speter xrealloc_and_strcat (&line, &line_len, "/"); 5471128269Speter xrealloc_and_strcat (&line, &line_len, node->key); 5472128269Speter delnode (node); 5473128269Speter } 5474128269Speter Entries_Close (entries); 5475128269Speter } 5476128269Speter 5477128269Speter /* If node is still NULL then we either didn't find CVSADM or 5478128269Speter * we didn't find an entry there. 5479128269Speter */ 5480128269Speter if (node == NULL) 5481128269Speter { 5482128269Speter /* Add the slash unless this is our first element. */ 5483128269Speter if (line_len) 5484128269Speter xrealloc_and_strcat (&line, &line_len, "/"); 5485128269Speter xrealloc_and_strcat (&line, &line_len, q); 5486128269Speter break; 5487128269Speter } 5488128269Speter 5489128269Speter /* And descend the tree. */ 5490128269Speter if (isdir (q)) 5491128269Speter CVS_CHDIR (q); 5492128269Speter free (q); 5493128269Speter } 5494128269Speter restore_cwd (&sdir, NULL); 5495128269Speter free_cwd (&sdir); 5496128269Speter 5497128269Speter /* Now put everything we didn't find entries for back on. */ 5498128269Speter while (q = pop (stack)) 5499128269Speter { 5500128269Speter if (line_len) 5501128269Speter xrealloc_and_strcat (&line, &line_len, "/"); 5502128269Speter xrealloc_and_strcat (&line, &line_len, q); 5503128269Speter free (q); 5504128269Speter } 5505128269Speter 5506128269Speter p = line; 5507128269Speter 5508128269Speter dellist (&stack); 550925839Speter } 5510128269Speter#else /* !FILENAMES_CASE_INSENSITIVE */ 5511128269Speter p = argv[i]; 551225839Speter#endif /* FILENAMES_CASE_INSENSITIVE */ 551325839Speter 551417721Speter send_to_server ("Argument ", 0); 551517721Speter 551617721Speter while (*p) 551717721Speter { 551817721Speter if (*p == '\n') 551917721Speter { 552017721Speter send_to_server ("\012Argumentx ", 0); 552117721Speter } 552217721Speter else if (ISDIRSEP (*p)) 552317721Speter { 552417721Speter buf[0] = '/'; 552517721Speter send_to_server (buf, 1); 552617721Speter } 552717721Speter else 552817721Speter { 552917721Speter buf[0] = *p; 553017721Speter send_to_server (buf, 1); 553117721Speter } 553217721Speter ++p; 553317721Speter } 553417721Speter send_to_server ("\012", 1); 5535128269Speter#ifdef FILENAMES_CASE_INSENSITIVE 5536128269Speter free (line); 5537128269Speter#endif /* FILENAMES_CASE_INSENSITIVE */ 553817721Speter } 553917721Speter 554017721Speter if (flags & SEND_EXPAND_WILD) 554117721Speter { 554217721Speter int i; 554317721Speter for (i = 0; i < argc; ++i) 554417721Speter free (argv[i]); 554517721Speter free (argv); 554617721Speter } 554717721Speter} 554817721Speter 554917721Speter 5550128269Speter 5551128269Speter/* Calculate and send max-dotdot to the server */ 5552128269Speterstatic void 5553128269Spetersend_max_dotdot (argc, argv) 5554128269Speter int argc; 5555128269Speter char **argv; 5556128269Speter{ 5557128269Speter int i; 5558128269Speter int level = 0; 5559128269Speter int max_level = 0; 5560128269Speter 5561128269Speter /* Send Max-dotdot if needed. */ 5562128269Speter for (i = 0; i < argc; ++i) 5563128269Speter { 5564128269Speter level = pathname_levels (argv[i]); 5565128269Speter if (level > 0) 5566128269Speter { 5567128269Speter if (uppaths == NULL) uppaths = getlist(); 5568128269Speter push_string (uppaths, xstrdup (argv[i])); 5569128269Speter } 5570128269Speter if (level > max_level) 5571128269Speter max_level = level; 5572128269Speter } 5573128269Speter 5574128269Speter if (max_level > 0) 5575128269Speter { 5576128269Speter if (supported_request ("Max-dotdot")) 5577128269Speter { 5578128269Speter char buf[10]; 5579128269Speter sprintf (buf, "%d", max_level); 5580128269Speter 5581128269Speter send_to_server ("Max-dotdot ", 0); 5582128269Speter send_to_server (buf, 0); 5583128269Speter send_to_server ("\012", 1); 5584128269Speter } 5585128269Speter else 5586128269Speter { 5587128269Speter error (1, 0, 5588128269Speter"backreference in path (`..') not supported by old (pre-Max-dotdot) servers"); 5589128269Speter } 5590128269Speter } 5591128269Speter} 5592128269Speter 5593128269Speter 5594128269Speter 559525839Speter/* Send Repository, Modified and Entry. argc and argv contain only 559625839Speter the files to operate on (or empty for everything), not options. 559725839Speter local is nonzero if we should not recurse (-l option). flags & 559825839Speter SEND_BUILD_DIRS is nonzero if nonexistent directories should be 559925839Speter sent. flags & SEND_FORCE is nonzero if we should send unmodified 560025839Speter files to the server as though they were modified. flags & 560125839Speter SEND_NO_CONTENTS means that this command only needs to know 560225839Speter _whether_ a file is modified, not the contents. Also sends Argument 560325839Speter lines for argc and argv, so should be called after options are sent. */ 560417721Spetervoid 560525839Spetersend_files (argc, argv, local, aflag, flags) 560617721Speter int argc; 560717721Speter char **argv; 560817721Speter int local; 560917721Speter int aflag; 561025839Speter unsigned int flags; 561117721Speter{ 561225839Speter struct send_data args; 561317721Speter int err; 561417721Speter 5615128269Speter send_max_dotdot (argc, argv); 5616128269Speter 561717721Speter /* 561817721Speter * aflag controls whether the tag/date is copied into the vers_ts. 561917721Speter * But we don't actually use it, so I don't think it matters what we pass 562017721Speter * for aflag here. 562117721Speter */ 562225839Speter args.build_dirs = flags & SEND_BUILD_DIRS; 562325839Speter args.force = flags & SEND_FORCE; 562425839Speter args.no_contents = flags & SEND_NO_CONTENTS; 562566528Speter args.backup_modified = flags & BACKUP_MODIFIED_FILES; 562617721Speter err = start_recursion 562717721Speter (send_fileproc, send_filesdoneproc, 562866528Speter send_dirent_proc, send_dirleave_proc, (void *) &args, 5629128269Speter argc, argv, local, W_LOCAL, aflag, CVS_LOCK_NONE, (char *) NULL, 0, 5630128269Speter (char *) NULL); 563117721Speter if (err) 563225839Speter error_exit (); 563317721Speter if (toplevel_repos == NULL) 563417721Speter /* 563517721Speter * This happens if we are not processing any files, 563617721Speter * or for checkouts in directories without any existing stuff 563717721Speter * checked out. The following assignment is correct for the 563817721Speter * latter case; I don't think toplevel_repos matters for the 563917721Speter * former. 564017721Speter */ 564181407Speter toplevel_repos = xstrdup (current_parsed_root->directory); 564217721Speter send_repository ("", toplevel_repos, "."); 564317721Speter} 564417721Speter 564517721Spetervoid 564617721Speterclient_import_setup (repository) 564717721Speter char *repository; 564817721Speter{ 564917721Speter if (toplevel_repos == NULL) /* should always be true */ 565017721Speter send_a_repository ("", repository, ""); 565117721Speter} 565217721Speter 565317721Speter/* 565417721Speter * Process the argument import file. 565517721Speter */ 565617721Speterint 565725839Speterclient_process_import_file (message, vfile, vtag, targc, targv, repository, 565854427Speter all_files_binary, modtime) 565917721Speter char *message; 566017721Speter char *vfile; 566117721Speter char *vtag; 566217721Speter int targc; 566317721Speter char *targv[]; 566417721Speter char *repository; 566525839Speter int all_files_binary; 566654427Speter 566754427Speter /* Nonzero for "import -d". */ 566854427Speter int modtime; 566917721Speter{ 567025839Speter char *update_dir; 567125839Speter char *fullname; 567225839Speter Vers_TS vers; 567317721Speter 567425839Speter assert (toplevel_repos != NULL); 567517721Speter 567617721Speter if (strncmp (repository, toplevel_repos, strlen (toplevel_repos)) != 0) 567717721Speter error (1, 0, 567817721Speter "internal error: pathname `%s' doesn't specify file in `%s'", 567917721Speter repository, toplevel_repos); 568017721Speter 568125839Speter if (strcmp (repository, toplevel_repos) == 0) 568217721Speter { 568325839Speter update_dir = ""; 568425839Speter fullname = xstrdup (vfile); 568517721Speter } 568625839Speter else 568725839Speter { 568825839Speter update_dir = repository + strlen (toplevel_repos) + 1; 568925839Speter 569025839Speter fullname = xmalloc (strlen (vfile) + strlen (update_dir) + 10); 569125839Speter strcpy (fullname, update_dir); 569225839Speter strcat (fullname, "/"); 569325839Speter strcat (fullname, vfile); 569425839Speter } 569525839Speter 569625839Speter send_a_repository ("", repository, update_dir); 569725839Speter if (all_files_binary) 569825839Speter { 569925839Speter vers.options = xmalloc (4); /* strlen("-kb") + 1 */ 570025839Speter strcpy (vers.options, "-kb"); 570125839Speter } 570225839Speter else 570325839Speter { 570425839Speter vers.options = wrap_rcsoption (vfile, 1); 570525839Speter } 570632785Speter if (vers.options != NULL) 570732785Speter { 570832785Speter if (supported_request ("Kopt")) 570932785Speter { 571032785Speter send_to_server ("Kopt ", 0); 571132785Speter send_to_server (vers.options, 0); 571232785Speter send_to_server ("\012", 1); 571332785Speter } 571432785Speter else 571532785Speter error (0, 0, 571632785Speter "warning: ignoring -k options due to server limitations"); 571732785Speter } 571854427Speter if (modtime) 571954427Speter { 572054427Speter if (supported_request ("Checkin-time")) 572154427Speter { 572254427Speter struct stat sb; 572354427Speter char *rcsdate; 572454427Speter char netdate[MAXDATELEN]; 572554427Speter 572654427Speter if (CVS_STAT (vfile, &sb) < 0) 572754427Speter error (1, errno, "cannot stat %s", fullname); 572854427Speter rcsdate = date_from_time_t (sb.st_mtime); 572954427Speter date_to_internet (netdate, rcsdate); 573054427Speter free (rcsdate); 573154427Speter 573254427Speter send_to_server ("Checkin-time ", 0); 573354427Speter send_to_server (netdate, 0); 573454427Speter send_to_server ("\012", 1); 573554427Speter } 573654427Speter else 573754427Speter error (0, 0, 573854427Speter "warning: ignoring -d option due to server limitations"); 573954427Speter } 574025839Speter send_modified (vfile, fullname, &vers); 574125839Speter if (vers.options != NULL) 574225839Speter free (vers.options); 574325839Speter free (fullname); 574417721Speter return 0; 574517721Speter} 574617721Speter 574717721Spetervoid 574817721Speterclient_import_done () 574917721Speter{ 575017721Speter if (toplevel_repos == NULL) 575117721Speter /* 575217721Speter * This happens if we are not processing any files, 575317721Speter * or for checkouts in directories without any existing stuff 575417721Speter * checked out. The following assignment is correct for the 575517721Speter * latter case; I don't think toplevel_repos matters for the 575617721Speter * former. 575717721Speter */ 575817721Speter /* FIXME: "can't happen" now that we call client_import_setup 575917721Speter at the beginning. */ 576081407Speter toplevel_repos = xstrdup (current_parsed_root->directory); 576117721Speter send_repository ("", toplevel_repos, "."); 576217721Speter} 5763128269Speter 5764128269Speter 5765128269Speter 576617721Speterstatic void 576717721Speternotified_a_file (data, ent_list, short_pathname, filename) 576817721Speter char *data; 576917721Speter List *ent_list; 577017721Speter char *short_pathname; 577117721Speter char *filename; 577217721Speter{ 577317721Speter FILE *fp; 577417721Speter FILE *newf; 577517721Speter size_t line_len = 8192; 577617721Speter char *line = xmalloc (line_len); 577717721Speter char *cp; 577817721Speter int nread; 577917721Speter int nwritten; 578017721Speter char *p; 578117721Speter 578217721Speter fp = open_file (CVSADM_NOTIFY, "r"); 578317721Speter if (getline (&line, &line_len, fp) < 0) 578417721Speter { 578532785Speter if (feof (fp)) 578632785Speter error (0, 0, "cannot read %s: end of file", CVSADM_NOTIFY); 578732785Speter else 578832785Speter error (0, errno, "cannot read %s", CVSADM_NOTIFY); 578917721Speter goto error_exit; 579017721Speter } 579117721Speter cp = strchr (line, '\t'); 579217721Speter if (cp == NULL) 579317721Speter { 579417721Speter error (0, 0, "malformed %s file", CVSADM_NOTIFY); 579517721Speter goto error_exit; 579617721Speter } 579717721Speter *cp = '\0'; 579817721Speter if (strcmp (filename, line + 1) != 0) 579917721Speter { 580017721Speter error (0, 0, "protocol error: notified %s, expected %s", filename, 580117721Speter line + 1); 580217721Speter } 580317721Speter 580417721Speter if (getline (&line, &line_len, fp) < 0) 580517721Speter { 580617721Speter if (feof (fp)) 580717721Speter { 580825839Speter free (line); 580917721Speter if (fclose (fp) < 0) 581017721Speter error (0, errno, "cannot close %s", CVSADM_NOTIFY); 581125839Speter if ( CVS_UNLINK (CVSADM_NOTIFY) < 0) 581217721Speter error (0, errno, "cannot remove %s", CVSADM_NOTIFY); 581317721Speter return; 581417721Speter } 581517721Speter else 581617721Speter { 581717721Speter error (0, errno, "cannot read %s", CVSADM_NOTIFY); 581817721Speter goto error_exit; 581917721Speter } 582017721Speter } 582117721Speter newf = open_file (CVSADM_NOTIFYTMP, "w"); 582217721Speter if (fputs (line, newf) < 0) 582317721Speter { 582417721Speter error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP); 582517721Speter goto error2; 582617721Speter } 582717721Speter while ((nread = fread (line, 1, line_len, fp)) > 0) 582817721Speter { 582917721Speter p = line; 583017721Speter while ((nwritten = fwrite (p, 1, nread, newf)) > 0) 583117721Speter { 583217721Speter nread -= nwritten; 583317721Speter p += nwritten; 583417721Speter } 583517721Speter if (ferror (newf)) 583617721Speter { 583717721Speter error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP); 583817721Speter goto error2; 583917721Speter } 584017721Speter } 584117721Speter if (ferror (fp)) 584217721Speter { 584317721Speter error (0, errno, "cannot read %s", CVSADM_NOTIFY); 584417721Speter goto error2; 584517721Speter } 584617721Speter if (fclose (newf) < 0) 584717721Speter { 584817721Speter error (0, errno, "cannot close %s", CVSADM_NOTIFYTMP); 584917721Speter goto error_exit; 585017721Speter } 585125839Speter free (line); 585217721Speter if (fclose (fp) < 0) 585317721Speter { 585417721Speter error (0, errno, "cannot close %s", CVSADM_NOTIFY); 585517721Speter return; 585617721Speter } 585717721Speter 585817721Speter { 585917721Speter /* In this case, we want rename_file() to ignore noexec. */ 586017721Speter int saved_noexec = noexec; 586117721Speter noexec = 0; 586217721Speter rename_file (CVSADM_NOTIFYTMP, CVSADM_NOTIFY); 586317721Speter noexec = saved_noexec; 586417721Speter } 586517721Speter 586617721Speter return; 586717721Speter error2: 586817721Speter (void) fclose (newf); 586917721Speter error_exit: 587025839Speter free (line); 587117721Speter (void) fclose (fp); 587217721Speter} 587317721Speter 587417721Speterstatic void 587517721Speterhandle_notified (args, len) 587617721Speter char *args; 587717721Speter int len; 587817721Speter{ 587917721Speter call_in_directory (args, notified_a_file, NULL); 588017721Speter} 588117721Speter 588217721Spetervoid 588317721Speterclient_notify (repository, update_dir, filename, notif_type, val) 5884128269Speter const char *repository; 5885128269Speter const char *update_dir; 5886128269Speter const char *filename; 588717721Speter int notif_type; 5888128269Speter const char *val; 588917721Speter{ 589017721Speter char buf[2]; 589117721Speter 589217721Speter send_a_repository ("", repository, update_dir); 589317721Speter send_to_server ("Notify ", 0); 589417721Speter send_to_server (filename, 0); 589517721Speter send_to_server ("\012", 1); 589617721Speter buf[0] = notif_type; 589717721Speter buf[1] = '\0'; 589817721Speter send_to_server (buf, 1); 589917721Speter send_to_server ("\t", 1); 590017721Speter send_to_server (val, 0); 590117721Speter} 590217721Speter 590317721Speter/* 590417721Speter * Send an option with an argument, dealing correctly with newlines in 590517721Speter * the argument. If ARG is NULL, forget the whole thing. 590617721Speter */ 590717721Spetervoid 590817721Speteroption_with_arg (option, arg) 590917721Speter char *option; 591017721Speter char *arg; 591117721Speter{ 591217721Speter if (arg == NULL) 591317721Speter return; 591417721Speter 591517721Speter send_to_server ("Argument ", 0); 591617721Speter send_to_server (option, 0); 591717721Speter send_to_server ("\012", 1); 591817721Speter 591917721Speter send_arg (arg); 592017721Speter} 592117721Speter 592244852Speter/* Send a date to the server. The input DATE is in RCS format. 592344852Speter The time will be GMT. 592417721Speter 592544852Speter We then convert that to the format required in the protocol 592644852Speter (including the "-D" option) and send it. According to 592754427Speter cvsclient.texi, RFC 822/1123 format is preferred. */ 592844852Speter 592917721Spetervoid 593017721Speterclient_senddate (date) 593117721Speter const char *date; 593217721Speter{ 593354427Speter char buf[MAXDATELEN]; 593417721Speter 593554427Speter date_to_internet (buf, (char *)date); 593617721Speter option_with_arg ("-D", buf); 593717721Speter} 593817721Speter 593917721Spetervoid 594017721Spetersend_init_command () 594117721Speter{ 594281407Speter /* This is here because we need the current_parsed_root->directory variable. */ 594317721Speter send_to_server ("init ", 0); 594481407Speter send_to_server (current_parsed_root->directory, 0); 594517721Speter send_to_server ("\012", 0); 594617721Speter} 594717721Speter 594817721Speter#endif /* CLIENT_SUPPORT */ 5949