-# $Id: Makefile.bsd-wrapper,v 1.7 1996/01/01 23:51:27 deraadt Exp $
+# $Id: Makefile.bsd-wrapper,v 1.8 1996/01/30 01:09:32 tholo Exp $
MAN= man/cvs.1 man/cvs.5 man/cvsbug.8 man/cvsinit.8 man/mkmodules.1
.FORCE: .IGNORE
config: .FORCE
- sh ${.CURDIR}/configure --prefix=/usr
+ sh ${.CURDIR}/configure --prefix=/usr --with-krb4=/usr
config.status:
- sh ${.CURDIR}/configure --prefix=/usr
+ sh ${.CURDIR}/configure --prefix=/usr --with-krb4=/usr
install: maninstall
${MAKE} prefix=${DESTDIR}/usr infodir=${DESTDIR}/usr/share/info \
BUGS MINOR-BUGS FAQ \
ChangeLog NEWS ChangeLog.zoo \
configure configure.in stamp-h.in config.h.in Makefile.in acconfig.h \
+ config.sub config.guess \
cvs-format.el mkinstalldirs install-sh cvsinit.sh \
cvsnt.mak \
.cvsignore
# Subdirectories to run make in for the primary targets.
INSTALL_MAN=man
-SUBDIRS = lib src ${INSTALL_MAN} doc contrib examples windows-NT
+SUBDIRS = lib src ${INSTALL_MAN} doc contrib examples windows-NT os2 macintosh
# Only make TAGS/tags files in these directories, in this order
TSUBDIRS= src lib
.PHONY: check
check:
+ cd lib ; $(MAKE) $(FLAGS_TO_PASS)
cd src ; $(MAKE) $(FLAGS_TO_PASS) check
.PHONY: remotecheck
remotecheck:
+ cd lib ; $(MAKE) $(FLAGS_TO_PASS)
cd src ; $(MAKE) $(FLAGS_TO_PASS) remotecheck
.PHONY: installcheck
installcheck:
+ cd lib ; $(MAKE) $(FLAGS_TO_PASS)
cd src ; $(MAKE) $(FLAGS_TO_PASS) installcheck
.PHONY: lint
.PHONY: dist
GZIP=gzip --best
GZIP_EXT=.gz
+TAR_VERBOSE=
dist:
echo > .fname \
cvs-`sed < $(srcdir)/src/version.c \
${MAKE} dist-dir DISTDIR="$${DISTDIR}" \
); \
done
- tar chvf - `cat .fname` | ${GZIP} > "`cat .fname`.tar${GZIP_EXT}"
+ tar chf${TAR_VERBOSE} - `cat .fname` | ${GZIP} > "`cat .fname`.tar${GZIP_EXT}"
rm -rf `cat .fname` .fname
.PHONY: dist-dir
+++ /dev/null
-;;;; @(#) Id: compile-all.el,v 1.11 1993/05/31 18:40:25 ceder Exp
-;;;; This file byte-compiles all .el files in pcl-cvs release 1.05.
-;;;;
-;;;; Copyright (C) 1991 Inge Wallin
-;;;;
-;;;; This file was once upon a time part of Elib, but have since been
-;;;; modified by Per Cederqvist.
-;;;;
-;;;; GNU Elib is free software; you can redistribute it and/or modify
-;;;; it under the terms of the GNU General Public License as published by
-;;;; the Free Software Foundation; either version 1, or (at your option)
-;;;; any later version.
-;;;;
-;;;; GNU Elib is distributed in the hope that it will be useful,
-;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;;;; GNU General Public License for more details.
-;;;;
-;;;; You should have received a copy of the GNU General Public License
-;;;; along with GNU Emacs; see the file COPYING. If not, write to
-;;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-;;;;
-
-
-(setq files-to-compile '("pcl-cvs" "pcl-cvs-lucid"))
-
-
-(defun compile-file-if-necessary (file)
- "Compile FILE if necessary.
-
-This is done if FILE.el is newer than FILE.elc or if FILE.elc doesn't exist."
- (let ((el-name (concat file ".el"))
- (elc-name (concat file ".elc")))
- (if (or (not (file-exists-p elc-name))
- (file-newer-than-file-p el-name elc-name))
- (progn
- (message (format "Byte-compiling %s..." el-name))
- (byte-compile-file el-name)))))
-
-
-(defun compile-pcl-cvs ()
- "Byte-compile all uncompiled files of pcl-cvs."
-
- (interactive)
-
- ;; Be sure to have . in load-path since a number of files
- ;; depend on other files and we always want the newer one even if
- ;; a previous version of pcl-cvs exists.
- (let ((load-path (append '(".") load-path)))
-
- (mapcar (function compile-file-if-necessary)
- files-to-compile)))
+++ /dev/null
-#!/bin/sh
-#
-# Combine a directory into a single tar package.
-#
-# This script is always called with the current directory set to
-# where the file to be combined exists. but i may get called with a
-# path to where cvs first started executing. (this probably should be
-# fixed in cvs) so strip out all of the directory information. The
-# first sed expression will only work if the path has a leading /
-# if it doesn't the one in the if statement will work.
-DIRNAME=`echo $1 | sed -e "s|/.*/||g"`
-if [ ! -d $DIRNAME ] ; then
- DIRNAME=`echo $1 | sed -e "s|.*/||g"`
-fi
-#
-# Now tar up the directory but we now will only get a relative path
-# even if the user did a cvs commit . at the top.
-#
-gnutar --preserve --sparse -cf - $DIRNAME | gzip --no-name --best -c > $2
+++ /dev/null
-#!/bin/sh
-#
-# extract the combined package (created with comb)
-# into the individual components.
-
-# move the file to a new name with an extension
-rm -rf $1.cvswrap
-mv $1 $1.cvswrap
-
-# untar the file
-
-if `gzip -t $1.cvswrap > /dev/null 2>&1`
-then
- gzcat -d $1.cvswrap | gnutar --preserve --sparse -x -f -
-else
- gnutar --preserve --sparse -x -f $1.cvswrap
-fi
-
-# remove the original
-rm -rf $1.cvswrap
+++ /dev/null
-/* error.c -- error handler for noninteractive utilities
- Copyright (C) 1990-1992 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
-/* David MacKenzie */
-/* Brian Berliner added support for CVS */
-
-#ifndef lint
-static char rcsid[] = "$CVSid: @(#)error.c 1.13 94/09/30 $";
-#endif /* not lint */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdio.h>
-
-#ifdef CVS_SUPPORT
-/* If non-zero, error will use the CVS protocol to report error
- messages. This will only be set in the CVS server parent process;
- most other code is run via do_cvs_command, which forks off a child
- process and packages up its stderr in the protocol.
-
- This really is ugly (why should lib/error.c know about the
- protocol??), but it's better than a mass munging of all the calls
- to error in modules.c. */
-int error_use_protocol;
-#endif
-
-#ifdef HAVE_VPRINTF
-
-#if __STDC__
-#include <stdarg.h>
-#define VA_START(args, lastarg) va_start(args, lastarg)
-#else
-#include <varargs.h>
-#define VA_START(args, lastarg) va_start(args)
-#endif
-
-#else
-
-#ifdef HAVE_DOPRNT
-#define va_alist args
-#define va_dcl int args;
-#else
-#define va_alist a1, a2, a3, a4, a5, a6, a7, a8
-#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
-#endif
-
-#endif
-
-#if STDC_HEADERS
-#include <stdlib.h>
-#include <string.h>
-#else
-#if __STDC__
-void exit(int status);
-#else
-void exit ();
-#endif /* __STDC__ */
-#endif
-
-extern char *strerror ();
-
-typedef void (*fn_returning_void) ();
-
-/* Function to call before exiting. */
-static fn_returning_void cleanup_fn;
-
-fn_returning_void
-error_set_cleanup (arg)
- fn_returning_void arg;
-{
- fn_returning_void retval = cleanup_fn;
- cleanup_fn = arg;
- return retval;
-}
-
-/* Print the program name and error message MESSAGE, which is a printf-style
- format string with optional args.
- If ERRNUM is nonzero, print its corresponding system error message.
- Exit with status STATUS if it is nonzero. */
-/* VARARGS */
-void
-#if defined (HAVE_VPRINTF) && __STDC__
-error (int status, int errnum, char *message, ...)
-#else
-error (status, errnum, message, va_alist)
- int status;
- int errnum;
- char *message;
- va_dcl
-#endif
-{
- FILE *out = stderr;
- extern char *program_name;
-#ifdef CVS_SUPPORT
- extern char *command_name;
-#endif
-#ifdef HAVE_VPRINTF
- va_list args;
-#endif
-
-#ifdef CVS_SUPPORT
- if (error_use_protocol)
- {
- out = stdout;
- printf ("E ");
- }
-#endif
-
-#ifdef CVS_SUPPORT
- if (command_name && *command_name)
- if (status)
- fprintf (out, "%s [%s aborted]: ", program_name, command_name);
- else
- fprintf (out, "%s %s: ", program_name, command_name);
- else
- fprintf (out, "%s: ", program_name);
-#else
- fprintf (out, "%s: ", program_name);
-#endif
-#ifdef HAVE_VPRINTF
- VA_START (args, message);
- vfprintf (out, message, args);
- va_end (args);
-#else
-#ifdef HAVE_DOPRNT
- _doprnt (message, &args, out);
-#else
- fprintf (out, message, a1, a2, a3, a4, a5, a6, a7, a8);
-#endif
-#endif
- if (errnum)
- fprintf (out, ": %s", strerror (errnum));
- putc ('\n', out);
- fflush (out);
- if (status)
- {
- if (cleanup_fn)
- (*cleanup_fn) ();
- exit (status);
- }
-}
-
-#ifdef CVS_SUPPORT
-
-/* Print the program name and error message MESSAGE, which is a printf-style
- format string with optional args to the file specified by FP.
- If ERRNUM is nonzero, print its corresponding system error message.
- Exit with status STATUS if it is nonzero. */
-/* VARARGS */
-void
-#if defined (HAVE_VPRINTF) && __STDC__
-fperror (FILE *fp, int status, int errnum, char *message, ...)
-#else
-fperror (fp, status, errnum, message, va_alist)
- FILE *fp;
- int status;
- int errnum;
- char *message;
- va_dcl
-#endif
-{
- extern char *program_name;
-#ifdef HAVE_VPRINTF
- va_list args;
-#endif
-
- fprintf (fp, "%s: ", program_name);
-#ifdef HAVE_VPRINTF
- VA_START (args, message);
- vfprintf (fp, message, args);
- va_end (args);
-#else
-#ifdef HAVE_DOPRNT
- _doprnt (message, &args, fp);
-#else
- fprintf (fp, message, a1, a2, a3, a4, a5, a6, a7, a8);
-#endif
-#endif
- if (errnum)
- fprintf (fp, ": %s", strerror (errnum));
- putc ('\n', fp);
- fflush (fp);
- if (status)
- {
- if (cleanup_fn)
- (*cleanup_fn) ();
- exit (status);
- }
-}
-
-#endif /* CVS_SUPPORT */
+++ /dev/null
-/* error.h -- declaration for error-reporting function
- Copyright (C) 1995 Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
-#ifndef _error_h_
-#define _error_h_
-
-#ifndef __attribute__
-/* This feature is available in gcc versions 2.5 and later. */
-# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__
-# define __attribute__(Spec) /* empty */
-# endif
-/* The __-protected variants of `format' and `printf' attributes
- are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */
-# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
-# define __format__ format
-# define __printf__ printf
-# endif
-#endif
-
-#if __STDC__
-void error (int, int, const char *, ...) \
- __attribute__ ((__format__ (__printf__, 3, 4)));
-#else
-void error ();
-#endif
-
-/* This variable is incremented each time `error' is called. */
-extern unsigned int error_message_count;
-
-#ifdef CVS_SUPPORT
-/* If non-zero, error will use the CVS protocol to report error
- messages. This will only be set in the CVS server parent process;
- most other code is run via do_cvs_command, which forks off a child
- process and packages up its stderr in the protocol. */
-extern int error_use_protocol;
-#endif
-
-#endif /* _error_h_ */
*/
#include "cvs.h"
+#include "getline.h"
+#include "edit.h"
+#include "fileattr.h"
#ifndef lint
static const char rcsid[] = "$CVSid: @(#)commit.c 1.101 94/10/07 $";
List * entries, List * srcfiles));
static int check_filesdoneproc PROTO((int err, char *repos, char *update_dir));
static int checkaddfile PROTO((char *file, char *repository, char *tag,
- List *srcfiles));
+ char *options, List *srcfiles));
static Dtype commit_direntproc PROTO((char *dir, char *repos, char *update_dir));
static int commit_dirleaveproc PROTO((char *dir, int err, char *update_dir));
static int commit_fileproc PROTO((char *file, char *update_dir, char *repository,
char *options, char *update_dir,
char *repository, List *entries));
static int findmaxrev PROTO((Node * p, void *closure));
-static int fsortcmp PROTO((const Node * p, const Node * q));
static int lock_RCS PROTO((char *user, char *rcs, char *rev, char *repository));
-static int lock_filesdoneproc PROTO((int err, char *repository, char *update_dir));
static int lockrcsfile PROTO((char *file, char *repository, char *rev));
static int precommit_list_proc PROTO((Node * p, void *closure));
static int precommit_proc PROTO((char *repository, char *filter));
static char *write_dirtag;
static char *logfile;
static List *mulist;
-static List *locklist;
static char *message;
static const char *const commit_usage[] =
NULL
};
+#ifdef CLIENT_SUPPORT
+struct find_data {
+ List *ulist;
+ int argc;
+ char **argv;
+};
+
+/* Pass as a static until we get around to fixing start_recursion to
+ pass along a void * where we can stash it. */
+struct find_data *find_data_static;
+
+static int find_fileproc PROTO ((char *, char *, char *, List *, List *));
+
+/* Machinery to find out what is modified, added, and removed. It is
+ possible this should be broken out into a new client_classify function;
+ merging it with classify_file is almost sure to be a mess, though,
+ because classify_file has all kinds of repository processing. */
+static int
+find_fileproc (file, update_dir, repository, entries, srcfiles)
+ char *file;
+ char *update_dir;
+ char *repository;
+ List *entries;
+ List *srcfiles;
+{
+ Vers_TS *vers;
+ enum classify_type status;
+ Node *node;
+ struct find_data *args = find_data_static;
+ char *fullname;
+
+ fullname = xmalloc (strlen (update_dir) + strlen (file) + 10);
+ fullname[0] = '\0';
+ if (update_dir[0] != '\0')
+ {
+ strcat (fullname, update_dir);
+ strcat (fullname, "/");
+ }
+ strcat (fullname, file);
+
+ vers = Version_TS ((char *)NULL, (char *)NULL, (char *)NULL,
+ (char *)NULL,
+ file, 0, 0, entries, (List *)NULL);
+ if (vers->ts_user == NULL
+ && vers->vn_user != NULL
+ && vers->vn_user[0] == '-')
+ status = T_REMOVED;
+ else if (vers->ts_user == NULL
+ && vers->vn_user == NULL)
+ {
+ error (0, 0, "nothing known about `%s'", fullname);
+ free (fullname);
+ return 1;
+ }
+ else if (vers->ts_user != NULL
+ && vers->vn_user != NULL
+ && vers->vn_user[0] == '0')
+ status = T_ADDED;
+ else if (vers->ts_user != NULL
+ && vers->ts_rcs != NULL
+ && strcmp (vers->ts_user, vers->ts_rcs) != 0)
+ status = T_MODIFIED;
+ else
+ {
+ /* This covers unmodified files, as well as a variety of other cases
+ (e.g. "touch foo", "cvs ci foo"). FIXME: we probably should be
+ printing a message and returning 1 for many of those cases (but
+ I'm not sure exactly which ones). */
+ free (fullname);
+ return 0;
+ }
+
+ node = getnode ();
+ node->key = xmalloc (strlen (update_dir) + strlen (file) + 8);
+ node->key[0] = '\0';
+ if (update_dir[0] != '\0')
+ {
+ strcpy (node->key, update_dir);
+ strcat (node->key, "/");
+ }
+ strcat (node->key, file);
+
+ node->type = UPDATE;
+ node->delproc = update_delproc;
+ node->data = (char *) status;
+ (void)addnode (args->ulist, node);
+
+ ++args->argc;
+
+ free (fullname);
+
+ return 0;
+}
+
+static int copy_ulist PROTO ((Node *, void *));
+
+static int
+copy_ulist (node, data)
+ Node *node;
+ void *data;
+{
+ struct find_data *args = (struct find_data *)data;
+ args->argv[args->argc++] = node->key;
+ return 0;
+}
+#endif /* CLIENT_SUPPORT */
+
int
commit (argc, argv)
int argc;
#ifdef CLIENT_SUPPORT
if (client_active)
{
+ struct find_data find_args;
+
+ ign_setup ();
+
+ /* Note that we don't do ignore file processing here, and we
+ don't call ignore_files. This means that we won't print "?
+ foo" for stray files. Sounds OK, the doc only promises
+ that update does that. */
+ find_args.ulist = getlist ();
+ find_args.argc = 0;
+ find_data_static = &find_args;
+ err = start_recursion (find_fileproc, (FILESDONEPROC) NULL,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
+ argc, argv, local, W_LOCAL, 0, 0,
+ (char *)NULL, 0, 0);
+ if (err)
+ error (1, 0, "correct above errors first!");
+
+ if (find_args.argc == 0)
+ return 0;
+
+ /* Now we keep track of which files we actually are going to
+ operate on, and only work with those files in the future.
+ This saves time--we don't want to search the file system
+ of the working directory twice. */
+ find_args.argv = (char **) xmalloc (find_args.argc * sizeof (char **));
+ find_args.argc = 0;
+ walklist (find_args.ulist, copy_ulist, &find_args);
+
/*
- * Do this now; don't ask for a log message if we can't talk to the
- * server. But if there is a syntax error in the options, give
- * an error message without connecting.
- */
+ * Do this before calling do_editor; don't ask for a log
+ * message if we can't talk to the server. But do it after we
+ * have made the checks that we can locally (to more quickly
+ * catch syntax errors, the case where no files are modified,
+ * added or removed, etc.). */
start_server ();
-
- ign_setup ();
/*
* We do this once, not once for each directory as in normal CVS.
* The protocol is designed this way. This is a feature.
- *
- * We could provide the lists of changed, modified, etc. files,
- * however. Our failure to do so is just laziness, not design.
*/
if (use_editor)
- do_editor (".", &message, (char *)NULL, (List *)NULL);
+ do_editor (".", &message, (char *)NULL, find_args.ulist);
/* We always send some sort of message, even if empty. */
option_with_arg ("-m", message);
send_arg("-n");
option_with_arg ("-r", tag);
- send_files (argc, argv, local, 0);
+ /* Sending only the names of the files which were modified, added,
+ or removed means that the server will only do an up-to-date
+ check on those files. This is different from local CVS and
+ previous versions of client/server CVS, but it probably is a Good
+ Thing, or at least Not Such A Bad Thing. */
+ send_file_names (find_args.argc, find_args.argv);
+ send_files (find_args.argc, find_args.argv, local, 0);
- if (fprintf (to_server, "ci\n") < 0)
- error (1, errno, "writing to server");
+ send_to_server ("ci\012", 0);
return get_responses_and_close ();
}
#endif
+ if (tag != NULL)
+ tag_check_valid (tag, argc, argv, local, aflag, "");
+
/* XXX - this is not the perfect check for this */
if (argc <= 0)
write_dirtag = tag;
wrap_setup ();
- /*
- * Run the recursion processor to find all the dirs to lock and lock all
- * the dirs
- */
- locklist = getlist ();
- err = start_recursion ((int (*) ()) NULL, lock_filesdoneproc,
- (Dtype (*) ()) NULL, (int (*) ()) NULL, argc,
- argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0,
- 0);
- sortlist (locklist, fsortcmp);
- if (Writer_Lock (locklist) != 0)
- error (1, 0, "lock failed - giving up");
+ lock_tree_for_write (argc, argv, local, aflag);
/*
* Set up the master update list
* Run the recursion processor to verify the files are all up-to-date
*/
err = start_recursion (check_fileproc, check_filesdoneproc,
- check_direntproc, (int (*) ()) NULL, argc,
+ check_direntproc, (DIRLEAVEPROC) NULL, argc,
argv, local, W_LOCAL, aflag, 0, (char *) NULL, 1,
0);
if (err)
{
- Lock_Cleanup ();
+ lock_tree_cleanup ();
error (1, 0, "correct above errors first!");
}
/*
* Unlock all the dirs and clean up
*/
- Lock_Cleanup ();
+ lock_tree_cleanup ();
dellist (&mulist);
- dellist (&locklist);
- return (err);
-}
-
-/*
- * compare two lock list nodes (for sort)
- */
-static int
-fsortcmp (p, q)
- const Node *p;
- const Node *q;
-{
- return (strcmp (p->key, q->key));
-}
-
-/*
- * Create a list of repositories to lock
- */
-/* ARGSUSED */
-static int
-lock_filesdoneproc (err, repository, update_dir)
- int err;
- char *repository;
- char *update_dir;
-{
- Node *p;
-
- p = getnode ();
- p->type = LOCK;
- p->key = xstrdup (repository);
- /* FIXME-KRP: this error condition should not simply be passed by. */
- if (p->key == NULL || addnode (locklist, p) != 0)
- freenode (p);
return (err);
}
* If the timestamps differ, look for Conflict indicators
* in the file to see if we should block the commit anyway
*/
- run_setup ("%s -s", GREP);
+ run_setup ("%s", GREP);
run_arg (RCS_MERGE_PAT);
run_arg (file);
- retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+ retcode = run_exec (RUN_TTY, DEVNULL, RUN_TTY, RUN_REALLY);
if (retcode == -1)
{
}
else if (ci->status == T_ADDED)
{
- if (checkaddfile (file, repository, ci->tag, srcfiles) != 0)
+ if (checkaddfile (file, repository, ci->tag, ci->options,
+ srcfiles) != 0)
{
fixaddfile (file, repository);
err = 1;
#endif
}
+ /* Clearly this is right for T_MODIFIED. I haven't thought so much
+ about T_ADDED or T_REMOVED. */
+ notify_do ('C', file, getcaller (), NULL, NULL, repository);
+
out:
if (err != 0)
{
if (err == 0 && run_module_prog)
{
- char *cp;
FILE *fp;
- char line[MAXLINELEN];
- char *repository;
- /* It is not an error if Checkin.prog does not exist. */
if ((fp = fopen (CVSADM_CIPROG, "r")) != NULL)
{
- if (fgets (line, sizeof (line), fp) != NULL)
+ char *line;
+ int line_length;
+ size_t line_chars_allocated;
+ char *repository;
+
+ line = NULL;
+ line_chars_allocated = 0;
+ line_length = getline (&line, &line_chars_allocated, fp);
+ if (line_length > 0)
{
- if ((cp = strrchr (line, '\n')) != NULL)
- *cp = '\0';
+ /* Remove any trailing newline. */
+ if (line[line_length - 1] == '\n')
+ line[--line_length] = '\0';
repository = Name_Repository ((char *) NULL, update_dir);
run_setup ("%s %s", line, repository);
(void) printf ("%s %s: Executing '", program_name,
(void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
free (repository);
}
- (void) fclose (fp);
+ else
+ {
+ if (ferror (fp))
+ error (0, errno, "warning: error reading %s",
+ CVSADM_CIPROG);
+ }
+ if (line != NULL)
+ free (line);
+ if (fclose (fp) < 0)
+ error (0, errno, "warning: cannot close %s", CVSADM_CIPROG);
+ }
+ else
+ {
+ if (! existence_error (errno))
+ error (0, errno, "warning: cannot open %s", CVSADM_CIPROG);
}
}
{
/* no revision exists on this branch. use the previous
revision but do not lock. */
- corev = RCS_gettag (rcsfile, tag, 1);
+ corev = RCS_gettag (rcsfile, tag, 1, 0);
prev_rev = xstrdup(rev);
lockflag = "";
} else
strlen(file) +
sizeof(RCSEXT) + 1);
(void) sprintf (tmp, "%s/%s", repository, CVSATTIC);
- omask = umask (2);
+ omask = umask (cvsumask);
(void) CVS_MKDIR (tmp, 0777);
(void) umask (omask);
(void) sprintf (tmp, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
message, entries);
if (ret == 0)
{
- (void) sprintf (tmp, "%s/%s%s", CVSADM, file, CVSEXT_OPT);
- (void) unlink_file (tmp);
(void) sprintf (tmp, "%s/%s%s", CVSADM, file, CVSEXT_LOG);
(void) unlink_file (tmp);
}
*/
static int
-checkaddfile (file, repository, tag, srcfiles)
+checkaddfile (file, repository, tag, options, srcfiles)
char *file;
char *repository;
char *tag;
+ char *options;
List *srcfiles;
{
- FILE *fp;
- char *cp;
char rcs[PATH_MAX];
char fname[PATH_MAX];
mode_t omask;
if (tag)
{
(void) sprintf(rcs, "%s/%s", repository, CVSATTIC);
- omask = umask (2);
+ omask = umask (cvsumask);
if (CVS_MKDIR (rcs, 0777) != 0 && errno != EEXIST)
error (1, errno, "cannot make directory `%s'", rcs);;
(void) umask (omask);
}
rcsfile = (RCSNode *) p->data;
- rev = RCS_getversion (rcsfile, tag, NULL, 1);
+ rev = RCS_getversion (rcsfile, tag, NULL, 1, 0);
/* and lock it */
if (lock_RCS (file, rcs, rev, repository)) {
error (0, 0, "cannot lock `%s'.", rcs);
if (isfile (fname))
run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG);
- (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_OPT);
- fp = fopen (fname, "r");
- /* If the file does not exist, no big deal. In particular, the
- server does not (yet at least) create CVSEXT_OPT files. */
- if (fp == NULL)
- {
- if (errno != ENOENT)
- error (1, errno, "cannot open %s", fname);
- }
- else
- {
- while (fgets (fname, sizeof (fname), fp) != NULL)
- {
- if ((cp = strrchr (fname, '\n')) != NULL)
- *cp = '\0';
- if (*fname)
- run_arg (fname);
- }
- (void) fclose (fp);
- }
+ /* Set RCS keyword expansion options. */
+ if (options && options[0] == '-' && options[1] == 'k')
+ run_arg (options);
run_arg (rcs);
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
{
to create a dead revision on the trunk. */
if (tag && newfile)
{
- char tmp[PATH_MAX];
+ char *tmp;
/* move the new file out of the way. */
(void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file);
rename_file (file, fname);
copy_file (DEVNULL, file);
-
+
+ tmp = xmalloc (strlen (file) + strlen (tag) + 80);
/* commit a dead revision. */
- (void) sprintf (tmp, "-mfile %s was initially added on branch %s.", file, tag);
+ (void) sprintf (tmp, "-mfile %s was initially added on branch %s.",
+ file, tag);
#ifdef DEATH_STATE
run_setup ("%s%s -q -f -sdead", Rcsbin, RCS_CI);
#else
run_setup ("%s%s -q -K", Rcsbin, RCS_CI);
#endif
run_arg (tmp);
+ free (tmp);
run_arg (rcs);
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
{
char *head;
char *magicrev;
- head = RCS_getversion (rcsfile, NULL, NULL, 0);
+ head = RCS_getversion (rcsfile, NULL, NULL, 0, 0);
magicrev = RCS_magicrev (rcsfile, head);
if ((retcode = RCS_settag(rcs, tag, magicrev)) != 0)
{
#else /* No DEATH_SUPPORT */
run_setup ("%s%s -i", Rcsbin, RCS);
run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG);
- (void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_OPT);
- fp = open_file (fname, "r");
- while (fgets (fname, sizeof (fname), fp) != NULL)
- {
- if ((cp = strrchr (fname, '\n')) != NULL)
- *cp = '\0';
- if (*fname)
- run_arg (fname);
- }
- (void) fclose (fp);
+ /* Set RCS keyword expansion options. */
+ if (options && options[0] == '-' && options[1] == 'k')
+ run_arg (options);
run_arg (rcs);
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) != 0)
{
}
#endif /* No DEATH_SUPPORT */
+ fileattr_newfile (file);
+
fix_rcs_modes (rcs, file);
return (0);
}
#ifdef _AIX
#pragma alloca
#else /* not _AIX */
+#ifdef ALLOCA_IN_STDLIB
+ /* then we need do nothing */
+#else
char *alloca ();
+#endif /* not ALLOCA_IN_STDLIB */
#endif /* not _AIX */
#endif /* not HAVE_ALLOCA_H */
#endif /* not __GNUC__ */
#include <stdio.h>
+/* Under OS/2, <stdio.h> doesn't define popen()/pclose(). */
+#ifdef USE_OWN_POPEN
+#include "popen.h"
+#endif
+
#ifdef STDC_HEADERS
#include <stdlib.h>
#else
char *strerror ();
#endif
-#include <fnmatch.h> /* This is supposed to be available on Posix systems */
+#include <fnmatch.h> /* This is supposed to be available on Posix systems */
#include <ctype.h>
#include <pwd.h>
#define CVSADM_CIPROG "CVS/Checkin.prog"
#define CVSADM_UPROG "CVS/Update.prog"
#define CVSADM_TAG "CVS/Tag"
+#define CVSADM_NOTIFY "CVS/Notify"
+#define CVSADM_NOTIFYTMP "CVS/Notify.tmp"
+/* A directory in which we store base versions of files we currently are
+ editing with "cvs edit". */
+#define CVSADM_BASE "CVS/Base"
+
+/* This is the special directory which we use to store various extra
+ per-directory information in the repository. It must be the same as
+ CVSADM to avoid creating a new reserved directory name which users cannot
+ use, but is a separate #define because if anyone changes it (which I don't
+ recommend), one needs to deal with old, unconverted, repositories.
+
+ See fileattr.h for details about file attributes, the only thing stored
+ in CVSREP currently. */
+#define CVSREP "CVS"
/*
* Definitions for the CVSROOT Administrative directory and the files it
#define CVSROOTADM_TAGINFO "taginfo"
#define CVSROOTADM_EDITINFO "editinfo"
#define CVSROOTADM_HISTORY "history"
+#define CVSROOTADM_VALTAGS "val-tags"
#define CVSROOTADM_IGNORE "cvsignore"
#define CVSROOTADM_CHECKOUTLIST "checkoutlist"
-#define CVSROOTADM_WRAPPER "cvswrappers"
+#define CVSROOTADM_WRAPPER "cvswrappers"
+#define CVSROOTADM_NOTIFY "notify"
+#define CVSROOTADM_USERS "users"
+
#define CVSNULLREPOS "Emptydir" /* an empty directory */
/* support for the modules file (CVSROOTADM_MODULES) */
#define CVSRFL "#cvs.rfl"
#define CVSWFL "#cvs.wfl"
#define CVSRFLPAT "#cvs.rfl.*" /* wildcard expr to match read locks */
-#define CVSEXT_OPT ",p"
#define CVSEXT_LOG ",t"
#define CVSPREFIX ",,"
#define CVSDOTIGNORE ".cvsignore"
#define IGNORE_ENV "CVSIGNORE" /* More files to ignore */
#define WRAPPER_ENV "CVSWRAPPERS" /* name of the wrapper file */
+#define CVSUMASK_ENV "CVSUMASK" /* Effective umask for repository */
+/* #define CVSUMASK_DFLT Set by config.h */
+
/*
* If the beginning of the Repository matches the following string, strip it
* so that the output to the logfile does not contain a full pathname.
/* structure of a entry record */
struct entnode
{
+ char *user;
char *version;
char *timestamp;
char *options;
*/
struct vers_ts
{
- char *vn_user; /* rcs version user file derives from
- * it can have the following special
- * values:
- * empty = no user file
- * 0 = user file is new
- * -vers = user file to be removed */
- char *vn_rcs; /* the version for the rcs file
- * (tag version?) */
- char *ts_user; /* the timestamp for the user file */
- char *ts_rcs; /* the user timestamp from entries */
- char *options; /* opts from Entries file
- * (keyword expansion) */
- char *ts_conflict; /* Holds time_stamp of conflict */
- char *tag; /* tag stored in the Entries file */
- char *date; /* date stored in the Entries file */
- Entnode *entdata; /* pointer to entries file node */
- RCSNode *srcfile; /* pointer to parsed src file info */
+ /* rcs version user file derives from, from CVS/Entries.
+ * it can have the following special values:
+ * empty = no user file
+ * 0 = user file is new
+ * -vers = user file to be removed. */
+ char *vn_user;
+
+ /* Numeric revision number corresponding to ->vn_tag (->vn_tag
+ will often be symbolic). */
+ char *vn_rcs;
+ /* If ->tag corresponds to a tag which really exists in this file,
+ this is just a copy of ->tag. If not, this is either NULL or
+ the head revision. (Or something like that, see RCS_getversion
+ and friends). */
+ char *vn_tag;
+
+ /* This is the timestamp from stating the file in the working directory.
+ It is NULL if there is no file in the working directory. */
+ char *ts_user;
+ /* Timestamp from CVS/Entries. For the server, ts_user and ts_rcs
+ are computed in a slightly different way, but the fact remains that
+ if they are equal the file in the working directory is unmodified
+ and if they differ it is modified. */
+ char *ts_rcs;
+
+ /* Options from CVS/Entries (keyword expansion). */
+ char *options;
+
+ /* If non-NULL, there was a conflict (or merely a merge? See merge_file)
+ and the time stamp in this field is the time stamp of the working
+ directory file which was created with the conflict markers in it.
+ This is from CVS/Entries. */
+ char *ts_conflict;
+
+ /* Tag specified on the command line, or if none, tag stored in
+ CVS/Entries. */
+ char *tag;
+ /* Date specified on the command line, or if none, date stored in
+ CVS/Entries. */
+ char *date;
+
+ /* Pointer to entries file node */
+ Entnode *entdata;
+
+ /* Pointer to parsed src file info */
+ RCSNode *srcfile;
};
typedef struct vers_ts Vers_TS;
};
typedef enum direnter_type Dtype;
-extern char *program_name, *command_name;
+extern char *program_name, *program_path, *command_name;
extern char *Rcsbin, *Editor, *CVSroot;
#ifdef CVSADM_ROOT
extern char *CVSADM_Root;
extern int really_quiet, quiet;
extern int use_editor;
extern int cvswrite;
+extern mode_t cvsumask;
extern int trace; /* Show all commands */
extern int noexec; /* Don't modify disk anywhere */
char *xmalloc PROTO((size_t bytes));
char *xrealloc PROTO((char *ptr, size_t bytes));
char *xstrdup PROTO((const char *str));
+void strip_trailing_newlines PROTO((char *str));
int No_Difference PROTO((char *file, Vers_TS * vers, List * entries,
char *repository, char *update_dir));
-int Parse_Info PROTO((char *infofile, char *repository, int PROTO((*callproc)) PROTO(()), int all));
+typedef int (*CALLPROC) PROTO((char *repository, char *value));
+int Parse_Info PROTO((char *infofile, char *repository, CALLPROC callproc, int all));
int Reader_Lock PROTO((char *xrepository));
-int SIG_register PROTO((int sig, RETSIGTYPE PROTO((*fn)) PROTO(())));
+typedef RETSIGTYPE (*SIGCLEANUPPROC) PROTO(());
+int SIG_register PROTO((int sig, SIGCLEANUPPROC sigcleanup));
int Writer_Lock PROTO((List * list));
int ign_name PROTO((char *name));
int isdir PROTO((const char *file));
int islink PROTO((const char *file));
int isreadable PROTO((const char *file));
int iswritable PROTO((const char *file));
+int isaccessible PROTO((const char *file, const int mode));
int isabsolute PROTO((const char *filename));
char *last_component PROTO((char *path));
-int joining PROTO((void));
int numdots PROTO((const char *s));
int unlink_file PROTO((const char *f));
+int link_file PROTO ((const char *from, const char *to));
int unlink_file_dir PROTO((const char *f));
int update PROTO((int argc, char *argv[]));
int xcmp PROTO((const char *file1, const char *file2));
int yesno PROTO((void));
+void *valloc PROTO((size_t bytes));
time_t get_date PROTO((char *date, struct timeb *now));
void Create_Admin PROTO((char *dir, char *update_dir,
char *repository, char *tag, char *date));
+\f
void Lock_Cleanup PROTO((void));
+
+/* Writelock an entire subtree, well the part specified by ARGC, ARGV, LOCAL,
+ and AFLAG, anyway. */
+void lock_tree_for_write PROTO ((int argc, char **argv, int local, int aflag));
+
+/* Remove locks set by lock_tree_for_write. Currently removes readlocks
+ too. */
+void lock_tree_cleanup PROTO ((void));
+\f
void ParseTag PROTO((char **tagp, char **datep));
void Scratch_Entry PROTO((List * list, char *fname));
void WriteTag PROTO((char *dir, char *tag, char *date));
void fperror PROTO((FILE * fp, int status, int errnum, char *message,...));
void free_names PROTO((int *pargc, char *argv[]));
void freevers_ts PROTO((Vers_TS ** versp));
+
void ign_add PROTO((char *ign, int hold));
void ign_add_file PROTO((char *file, int hold));
void ign_setup PROTO((void));
void ign_dir_add PROTO((char *name));
int ignore_directory PROTO((char *name));
+typedef void (*Ignore_proc) PROTO ((char *, char *));
+extern void ignore_files PROTO ((List *, char *, Ignore_proc));
+extern int ign_inhibit_server;
+
+#include "update.h"
+
void line2argv PROTO((int *pargc, char *argv[], char *line));
void make_directories PROTO((const char *name));
void make_directory PROTO((const char *name));
int set_time, List * entries, List * xfiles));
void do_editor PROTO((char *dir, char **messagep,
char *repository, List * changes));
+
+typedef int (*CALLBACKPROC) PROTO((int *pargc, char *argv[], char *where,
+ char *mwhere, char *mfile, int horten, int local_specified,
+ char *omodule, char *msg));
+typedef int (*FILEPROC) PROTO((char *file, char *update_dir, char *repository,
+ List * entries, List * srcfiles));
+typedef int (*FILESDONEPROC) PROTO((int err, char *repository, char *update_dir));
+typedef Dtype (*DIRENTPROC) PROTO((char *dir, char *repos, char *update_dir));
+typedef int (*DIRLEAVEPROC) PROTO((char *dir, int err, char *update_dir));
+
int do_module PROTO((DBM * db, char *mname, enum mtype m_type, char *msg,
- int PROTO((*callback_proc)) (), char *where, int shorten,
- int local_specified, int run_module_prog, char *extra_arg));
-int do_recursion PROTO((int PROTO((*xfileproc)) (), int PROTO((*xfilesdoneproc)) (),
- Dtype PROTO((*xdirentproc)) (), int PROTO((*xdirleaveproc)) (),
+ CALLBACKPROC callback_proc, char *where, int shorten,
+ int local_specified, int run_module_prog, char *extra_arg));
+int do_recursion PROTO((FILEPROC xfileproc, FILESDONEPROC xfilesdoneproc,
+ DIRENTPROC xdirentproc, DIRLEAVEPROC xdirleaveproc,
Dtype xflags, int xwhich, int xaflag, int xreadlock,
int xdosrcs));
-int do_update PROTO((int argc, char *argv[], char *xoptions, char *xtag,
- char *xdate, int xforce, int local, int xbuild,
- int xaflag, int xprune, int xpipeout, int which,
- char *xjoin_rev1, char *xjoin_rev2, char *preload_update_dir));
void history_write PROTO((int type, char *update_dir, char *revs, char *name,
char *repository));
-int start_recursion PROTO((int PROTO((*fileproc)) (), int PROTO((*filesdoneproc)) (),
- Dtype PROTO((*direntproc)) (), int PROTO((*dirleaveproc)) (),
+int start_recursion PROTO((FILEPROC fileproc, FILESDONEPROC filesdoneproc,
+ DIRENTPROC direntproc, DIRLEAVEPROC dirleaveproc,
int argc, char *argv[], int local, int which,
int aflag, int readlock, char *update_preload,
int dosrcs, int wd_is_repos));
int filter_stream_through_program PROTO((int, int, char **, pid_t *));
pid_t waitpid PROTO((pid_t, int *, int));
-
+\f
/* Wrappers. */
typedef enum { WRAP_MERGE, WRAP_COPY } WrapMergeMethod;
char *wrap_tocvs_process_file PROTO((const char *fileName));
int wrap_merge_is_copy PROTO((const char *fileName));
char *wrap_fromcvs_process_file PROTO((const char *fileName));
+/* Pathname expansion */
+char *expand_path PROTO((char *name));
void wrap_add_file PROTO((const char *file,int temp));
void wrap_add PROTO((char *line,int temp));
+\f
+int watch PROTO ((int argc, char **argv));
+int edit PROTO ((int argc, char **argv));
+int unedit PROTO ((int argc, char **argv));
+int editors PROTO ((int argc, char **argv));
+int watchers PROTO ((int argc, char **argv));
+\f
+#if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT)
+char *scramble PROTO ((char *str));
+char *descramble PROTO ((char *str));
+#endif /* AUTH_CLIENT_SUPPORT || AUTH_SERVER_SUPPORT */
+
+extern void tag_check_valid PROTO ((char *, int, char **, int, int, char *));
USE(rcsid);
#endif
-extern char *ctime ();
-
static int readers_exist PROTO((char *repository));
static int set_lock PROTO((char *repository, int will_wait));
static void clear_lock PROTO((void));
static int write_lock PROTO((char *repository));
static void unlock PROTO((char *repository));
static void lock_wait PROTO((char *repository));
+static int Check_Owner PROTO((char *lockdir));
static char lockers_name[20];
static char *repository;
char *repository;
{
char tmp[PATH_MAX];
- struct stat sb;
if (readlock[0] != '\0')
{
(void) sprintf (tmp, "%s/%s", repository, readlock);
- if (unlink (tmp) < 0 && errno != ENOENT)
+ if (unlink (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
}
if (writelock[0] != '\0')
{
(void) sprintf (tmp, "%s/%s", repository, writelock);
- if (unlink (tmp) < 0 && errno != ENOENT)
+ if (unlink (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
}
*/
if (writelock[0] != '\0' || (readlock[0] != '\0' && cleanup_lckdir))
{
- (void) sprintf (tmp, "%s/%s", repository, CVSLCK);
- if (stat (tmp, &sb) != -1 && sb.st_uid == geteuid ())
- {
+ (void) sprintf (tmp, "%s/%s", repository, CVSLCK);
+ if (Check_Owner(tmp))
+ {
+#ifdef AFSCVS
+ char rmuidlock[PATH_MAX];
+ sprintf(rmuidlock, "rm -f %s/uidlock%d", tmp, geteuid() );
+ system(rmuidlock);
+#endif
(void) rmdir (tmp);
- }
+ }
}
cleanup_lckdir = 0;
}
+/*
+ * Check the owner of a lock. Returns 1 if we own it, 0 otherwise.
+ */
+static int
+Check_Owner(lockdir)
+ char *lockdir;
+{
+ struct stat sb;
+
+#ifdef AFSCVS
+ /* In the Andrew File System (AFS), user ids from stat don't match
+ those from geteuid(). The AFSCVS code can deal with either AFS or
+ non-AFS repositories; the non-AFSCVS code is faster. */
+ char uidlock[PATH_MAX];
+
+ /* Check if the uidlock is in the lock directory */
+ sprintf(uidlock, "%s/uidlock%d", lockdir, geteuid() );
+ if( stat(uidlock, &sb) != -1)
+ return 1; /* The file exists, therefore we own the lock */
+ else
+ return 0; /* The file didn't exist or some other error.
+ * Assume that we don't own it.
+ */
+#else
+ if (stat (lockdir, &sb) != -1 && sb.st_uid == geteuid ())
+ return 1;
+ else
+ return 0;
+#endif
+} /* end Check_Owner() */
+
+
/*
* Create a lock file for readers
*/
error (0, errno, "cannot create read lock in repository `%s'",
xrepository);
readlock[0] = '\0';
- if (unlink (tmp) < 0 && errno != ENOENT)
+ if (unlink (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
return (1);
}
{
error (0, errno, "cannot create write lock in repository `%s'",
repository);
- if (unlink (tmp) < 0 && errno != ENOENT)
+ if (unlink (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
return (L_ERROR);
}
{
int xerrno = errno;
- if (unlink (tmp) < 0 && errno != ENOENT)
+ if (unlink (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
/* free the lock dir if we created it */
readers_exist (repository)
char *repository;
{
- char line[MAXLINELEN];
+ char *line;
DIR *dirp;
struct dirent *dp;
struct stat sb;
(void) time (&now);
#endif
+ line = xmalloc (strlen (repository) + strlen (dp->d_name) + 5);
(void) sprintf (line, "%s/%s", repository, dp->d_name);
if (stat (line, &sb) != -1)
{
if (now >= (sb.st_ctime + CVSLCKAGE) && unlink (line) != -1)
{
(void) closedir (dirp);
+ free (line);
goto again;
}
#endif
set_lockers_name (&sb);
}
+ free (line);
ret = 1;
break;
int will_wait;
{
struct stat sb;
+ mode_t omask;
#ifdef CVS_FUDGELOCKS
time_t now;
#endif
cleanup_lckdir = 0;
for (;;)
{
+ int status = -1;
+ omask = umask (cvsumask);
SIG_beginCrSect ();
if (CVS_MKDIR (masterlock, 0777) == 0)
{
+#ifdef AFSCVS
+ char uidlock[PATH_MAX];
+ FILE *fp;
+
+ sprintf(uidlock, "%s/uidlock%d", masterlock, geteuid() );
+ if ((fp = fopen(uidlock, "w+")) == NULL)
+ {
+ /* We failed to create the uidlock,
+ so rm masterlock and leave */
+ rmdir(masterlock);
+ SIG_endCrSect ();
+ status = L_ERROR;
+ goto out;
+ }
+
+ /* We successfully created the uid lock, so close the file */
+ fclose(fp);
+#endif
cleanup_lckdir = 1;
SIG_endCrSect ();
- return (L_OK);
+ status = L_OK;
+ goto out;
}
SIG_endCrSect ();
+ out:
+ (void) umask (omask);
+ if (status != -1)
+ return status;
if (errno != EEXIST)
{
*/
if (stat (masterlock, &sb) < 0)
{
- if (errno == ENOENT)
+ if (existence_error (errno))
continue;
error (0, errno, "couldn't stat lock directory `%s'", masterlock);
(void) time (&now);
if (now >= (sb.st_ctime + CVSLCKAGE))
{
+#ifdef AFSCVS
+ /* Remove the uidlock first */
+ char rmuidlock[PATH_MAX];
+ sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() );
+ system(rmuidlock);
+#endif
if (rmdir (masterlock) >= 0)
continue;
}
static void
clear_lock()
{
+#ifdef AFSCVS
+ /* Remove the uidlock first */
+ char rmuidlock[PATH_MAX];
+ sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() );
+ system(rmuidlock);
+#endif
if (rmdir (masterlock) < 0)
error (0, errno, "failed to remove lock dir `%s'", masterlock);
cleanup_lckdir = 0;
lockers_name, repos);
(void) sleep (CVSLCKSLEEP);
}
+\f
+static int lock_filesdoneproc PROTO ((int err, char *repository,
+ char *update_dir));
+static int fsortcmp PROTO((const Node * p, const Node * q));
+
+static List *lock_tree_list;
+
+/*
+ * Create a list of repositories to lock
+ */
+/* ARGSUSED */
+static int
+lock_filesdoneproc (err, repository, update_dir)
+ int err;
+ char *repository;
+ char *update_dir;
+{
+ Node *p;
+
+ p = getnode ();
+ p->type = LOCK;
+ p->key = xstrdup (repository);
+ /* FIXME-KRP: this error condition should not simply be passed by. */
+ if (p->key == NULL || addnode (lock_tree_list, p) != 0)
+ freenode (p);
+ return (err);
+}
+
+/*
+ * compare two lock list nodes (for sort)
+ */
+static int
+fsortcmp (p, q)
+ const Node *p;
+ const Node *q;
+{
+ return (strcmp (p->key, q->key));
+}
+
+void
+lock_tree_for_write (argc, argv, local, aflag)
+ int argc;
+ char **argv;
+ int local;
+ int aflag;
+{
+ int err;
+ /*
+ * Run the recursion processor to find all the dirs to lock and lock all
+ * the dirs
+ */
+ lock_tree_list = getlist ();
+ err = start_recursion ((FILEPROC) NULL, lock_filesdoneproc,
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, argc,
+ argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0,
+ 0);
+ sortlist (lock_tree_list, fsortcmp);
+ if (Writer_Lock (lock_tree_list) != 0)
+ error (1, 0, "lock failed - giving up");
+}
+
+void
+lock_tree_cleanup ()
+{
+ Lock_Cleanup ();
+ dellist (&lock_tree_list);
+}
* commit Checks files into the repository
* diff Runs diffs between revisions
* log Prints "rlog" information for files
+ * login Record user, host, repos, password
* add Adds an entry to the repository
* remove Removes an entry from the repository
* status Status info on the revisions
*/
#include "cvs.h"
-#include "patchlevel.h"
#if HAVE_KERBEROS
#include <sys/socket.h>
#endif
char *program_name;
-char *command_name = "";
+char *program_path;
+/*
+ * Initialize comamnd_name to "cvs" so that the first call to
+ * read_cvsrc tries to find global cvs options.
+ */
+char *command_name = "cvs";
/*
* Since some systems don't define this...
char hostname[MAXHOSTNAMELEN];
+#ifdef AUTH_CLIENT_SUPPORT
+int use_authenticating_server = FALSE;
+#endif /* AUTH_CLIENT_SUPPORT */
int use_editor = TRUE;
int use_cvsrc = TRUE;
int cvswrite = !CVSREAD_DFLT;
int noexec = FALSE;
int readonlyfs = FALSE;
int logoff = FALSE;
+mode_t cvsumask = UMASK_DFLT;
char *CurDir;
int history PROTO((int argc, char **argv));
int import PROTO((int argc, char **argv));
int cvslog PROTO((int argc, char **argv));
+#ifdef AUTH_CLIENT_SUPPORT
+int login PROTO((int argc, char **argv));
+#endif /* AUTH_CLIENT_SUPPORT */
int patch PROTO((int argc, char **argv));
int release PROTO((int argc, char **argv));
int cvsremove PROTO((int argc, char **argv));
#endif
CMD_ENTRY("add", "ad", "new", add, client_add),
-#ifndef CVS_NOADMIN
CMD_ENTRY("admin", "adm", "rcs", admin, client_admin),
-#endif
CMD_ENTRY("checkout", "co", "get", checkout, client_checkout),
CMD_ENTRY("commit", "ci", "com", commit, client_commit),
CMD_ENTRY("diff", "di", "dif", diff, client_diff),
+ CMD_ENTRY("edit", "edit", "edit", edit, client_edit),
+ CMD_ENTRY("editors", "editors","editors",editors, client_editors),
CMD_ENTRY("export", "exp", "ex", checkout, client_export),
CMD_ENTRY("history", "hi", "his", history, client_history),
CMD_ENTRY("import", "im", "imp", import, client_import),
CMD_ENTRY("log", "lo", "rlog", cvslog, client_log),
+#ifdef AUTH_CLIENT_SUPPORT
+ CMD_ENTRY("login", "logon", "lgn", login, login),
+#endif /* AUTH_CLIENT_SUPPORT */
CMD_ENTRY("rdiff", "patch", "pa", patch, client_rdiff),
CMD_ENTRY("release", "re", "rel", release, client_release),
CMD_ENTRY("remove", "rm", "delete", cvsremove, client_remove),
CMD_ENTRY("status", "st", "stat", status, client_status),
CMD_ENTRY("rtag", "rt", "rfreeze", rtag, client_rtag),
CMD_ENTRY("tag", "ta", "freeze", tag, client_tag),
+ CMD_ENTRY("unedit", "unedit","unedit", unedit, client_unedit),
CMD_ENTRY("update", "up", "upd", update, client_update),
-
+ CMD_ENTRY("watch", "watch", "watch", watch, client_watch),
+ CMD_ENTRY("watchers", "watchers","watchers",watchers,client_watchers),
#ifdef SERVER_SUPPORT
/*
* The client_func is also server because we might have picked up a
" -z # Use 'gzip -#' for net traffic if possible.\n",
#endif
"\n",
- " and where 'command' is:\n",
+ " and where 'command' is: add, admin, etc. (use the --help-commands\n",
+ " option for a list of commands)\n",
+ NULL,
+};
+
+static const char *const cmd_usage[] =
+{
+ "CVS commands are:\n",
" add Adds a new file/directory to the repository\n",
" admin Administration front end for rcs\n",
" checkout Checkout sources for editing\n",
" commit Checks files into the repository\n",
" diff Runs diffs between revisions\n",
+ " edit Get ready to edit a watched file\n",
+ " editors See who is editing a watched file\n",
" history Shows status of files and users\n",
" import Import sources into CVS, using vendor branches\n",
" export Export sources from CVS, similar to checkout\n",
" log Prints out 'rlog' information for files\n",
+#ifdef AUTH_CLIENT_SUPPORT
+ " login Prompt for password for authenticating server.\n",
+#endif /* AUTH_CLIENT_SUPPORT */
" rdiff 'patch' format diffs between releases\n",
" release Indicate that a Module is no longer in use\n",
" remove Removes an entry from the repository\n",
" status Status info on the revisions\n",
" tag Add a symbolic tag to checked out version of RCS file\n",
+ " unedit Undo an edit command\n",
" rtag Add a symbolic tag to the RCS file\n",
" update Brings work tree in sync with repository\n",
+ " watch Set watches\n",
+ " watchers See who is watching a file\n",
NULL,
};
}
static void
-error_cleanup ()
+error_cleanup PROTO((void))
{
Lock_Cleanup();
#ifdef SERVER_SUPPORT
#endif
}
-#define KF_GETOPT_LONG 1
-
int
main (argc, argv)
int argc;
{
extern char *version_string;
extern char *config_string;
- char *cp;
+ char *cp, *end;
const struct cmd *cm;
int c, err = 0;
- static int help = FALSE, version_flag = FALSE;
+ static int help = FALSE;
+ static int version_flag = FALSE;
+ static int help_commands = FALSE;
int rcsbin_update_env, cvs_update_env = 0;
- char tmp[PATH_MAX];
static struct option long_options[] =
{
{"help", 0, &help, TRUE},
{"version", 0, &version_flag, TRUE},
+ {"help-commands", 0, &help_commands, TRUE},
{0, 0, 0, 0}
};
/* `getopt_long' stores the option index here, but right now we
error_set_cleanup (error_cleanup);
+/* The socket subsystems on NT and OS2 must be initialized before use */
+#ifdef INITIALIZE_SOCKET_SUBSYSTEM
+ INITIALIZE_SOCKET_SUBSYSTEM();
+#endif /* INITIALIZE_SOCKET_SUBSYSTEM */
+
/*
* Just save the last component of the path for error messages
*/
+ program_path = xstrdup (argv[0]);
program_name = last_component (argv[0]);
CurDir = xmalloc (PATH_MAX);
cvswrite = FALSE;
if (getenv (CVSREADONLYFS_ENV))
readonlyfs = TRUE;
+ if ((cp = getenv (CVSUMASK_ENV)) != NULL)
+ {
+ /* FIXME: Should be accepting symbolic as well as numeric mask. */
+ cvsumask = strtol (cp, &end, 8) & 0777;
+ if (*end != '\0')
+ error (1, errno, "invalid umask value in %s (%s)",
+ CVSUMASK_ENV, cp);
+ }
+
+ /*
+ * Scan cvsrc file for global options.
+ */
+ read_cvsrc(&argc, &argv);
/* This has the effect of setting getopt's ordering to REQUIRE_ORDER,
which is what we need to distinguish between global options and
#endif
case '?':
default:
- usage (usg);
+ usage (usg);
}
}
if (version_flag == TRUE)
- {
+ {
(void) fputs (version_string, stdout);
(void) fputs (config_string, stdout);
- (void) sprintf (tmp, "Patch Level: %d\n", PATCHLEVEL);
- (void) fputs (tmp, stdout);
(void) fputs ("\n", stdout);
(void) fputs ("Copyright (c) 1993-1994 Brian Berliner\n", stdout);
(void) fputs ("Copyright (c) 1993-1994 david d `zoo' zuhn\n", stdout);
(void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
(void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
exit (0);
- }
+ }
+ else if (help_commands)
+ usage (cmd_usage);
argc -= optind;
argv += optind;
}
#endif /* HAVE_KERBEROS */
+
+#if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT)
+ if (strcmp (argv[0], "pserver") == 0)
+ {
+ /* Gets username and password from client, authenticates, then
+ switches to run as that user and sends an ACK back to the
+ client. */
+ authenticate_connection ();
+
+ /* Pretend we were invoked as a plain server. */
+ argv[0] = "server";
+ }
+#endif /* AUTH_SERVER_SUPPORT && SERVER_SUPPORT */
+
+
#ifdef CVSADM_ROOT
/*
* See if we are able to find a 'better' value for CVSroot in the
}
#ifdef CLIENT_SUPPORT
else if (!getenv ("CVS_IGNORE_REMOTE_ROOT"))
-#else
+#else /* ! CLIENT_SUPPORT */
else
-#endif
+#endif /* CLIENT_SUPPORT */
{
/*
* Now for the hard part, compare the two directories. If they
}
#endif /* CVSADM_ROOT */
+ /* CVSroot may need fixing up, if an access-method was specified,
+ * but not a user. Later code assumes that if CVSroot contains an
+ * access-method, then it also has a user. We print a warning and
+ * die if we can't guarantee that.
+ */
+ if (CVSroot
+ && *CVSroot
+ && (CVSroot[0] == ':')
+ && (strchr (CVSroot, '@') == NULL))
+ {
+ error (1, 0,
+ "must also give a username if specifying access method");
+ }
+
/*
* Specifying just the '-H' flag to the sub-command causes a Usage
* message to be displayed.
error (1, 0, "You don't have a %s environment variable",
CVSROOT_ENV);
(void) sprintf (path, "%s/%s", CVSroot, CVSROOTADM);
- if (access (path, R_OK | X_OK))
+ if (!isaccessible (path, R_OK | X_OK))
{
save_errno = errno;
#ifdef CLIENT_SUPPORT
}
(void) strcat (path, "/");
(void) strcat (path, CVSROOTADM_HISTORY);
- if (readonlyfs == 0 && isfile (path) && access (path, R_OK | W_OK))
+ if (readonlyfs == 0 && isfile (path) && !isaccessible (path, R_OK | W_OK))
{
save_errno = errno;
error (0, 0,
+++ /dev/null
-#define PATCHLEVEL 2
+#include <assert.h>
#include "cvs.h"
+#include "watch.h"
+#include "edit.h"
+#include "fileattr.h"
#ifdef SERVER_SUPPORT
int tag PROTO((int argc, char **argv));
int update PROTO((int argc, char **argv));
\f
-void server_cleanup PROTO((int sig));
/*
* This is where we stash stuff we are going to use. Format string
if (strcmp (rs->name, name) == 0)
return rs->status == rs_supported;
error (1, 0, "internal error: testing support for unknown response?");
+ /* NOTREACHED */
+ return 0;
}
static void
if (error_pending()) return;
(void) sprintf (path, "%s/%s", arg, CVSROOTADM);
- if (access (path, R_OK | X_OK))
+ if (!isaccessible (path, R_OK | X_OK))
{
save_errno = errno;
pending_error_text = malloc (80 + strlen (path));
}
(void) strcat (path, "/");
(void) strcat (path, CVSROOTADM_HISTORY);
- if (readonlyfs == 0 && isfile (path) && access (path, R_OK | W_OK))
+ if (readonlyfs == 0 && isfile (path) && !isaccessible (path, R_OK | W_OK))
{
save_errno = errno;
pending_error_text = malloc (80 + strlen (path));
server_temp_dir = p;
}
\f
+static char *dir_name;
+
static void
dirswitch (dir, repos)
char *dir;
char *repos;
{
- char *dirname;
int status;
FILE *f;
if (error_pending()) return;
- dirname = malloc (strlen (server_temp_dir) + strlen (dir) + 40);
- if (dirname == NULL)
+ if (dir_name != NULL)
+ free (dir_name);
+
+ dir_name = malloc (strlen (server_temp_dir) + strlen (dir) + 40);
+ if (dir_name == NULL)
{
pending_error = ENOMEM;
return;
}
- strcpy (dirname, server_temp_dir);
- strcat (dirname, "/");
- strcat (dirname, dir);
+ strcpy (dir_name, server_temp_dir);
+ strcat (dir_name, "/");
+ strcat (dir_name, dir);
- status = mkdir_p (dirname);
+ status = mkdir_p (dir_name);
if (status != 0
&& status != EEXIST)
{
pending_error = status;
- pending_error_text = malloc (80 + strlen(dirname));
- sprintf(pending_error_text, "E cannot mkdir %s", dirname);
+ pending_error_text = malloc (80 + strlen(dir_name));
+ sprintf(pending_error_text, "E cannot mkdir %s", dir_name);
return;
}
- if (chdir (dirname) < 0)
+ if (chdir (dir_name) < 0)
{
pending_error = errno;
- pending_error_text = malloc (80 + strlen(dirname));
- sprintf(pending_error_text, "E cannot change to %s", dirname);
+ pending_error_text = malloc (80 + strlen(dir_name));
+ sprintf(pending_error_text, "E cannot change to %s", dir_name);
return;
}
/*
sprintf(pending_error_text, "E cannot close %s", CVSADM_ENT);
return;
}
- free (dirname);
-}
+}
static void
serve_repository (arg)
}
}
\f
+struct notify_note {
+ /* Directory in which this notification happens. malloc'd*/
+ char *dir;
+
+ /* malloc'd. */
+ char *filename;
+
+ /* The following three all in one malloc'd block, pointed to by TYPE.
+ Each '\0' terminated. */
+ /* "E" or "U". */
+ char *type;
+ /* time+host+dir */
+ char *val;
+ char *watches;
+
+ struct notify_note *next;
+};
+
+static struct notify_note *notify_list;
+/* Used while building list, to point to the last node that already exists. */
+static struct notify_note *last_node;
+
+static void serve_notify PROTO ((char *));
+
+static void
+serve_notify (arg)
+ char *arg;
+{
+ struct notify_note *new;
+ char *data;
+
+ if (error_pending ()) return;
+
+ new = (struct notify_note *) malloc (sizeof (struct notify_note));
+ if (new == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ if (dir_name == NULL)
+ goto error;
+ new->dir = malloc (strlen (dir_name) + 1);
+ if (new->dir == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ strcpy (new->dir, dir_name);
+ new->filename = malloc (strlen (arg) + 1);
+ if (new->filename == NULL)
+ {
+ pending_error = ENOMEM;
+ return;
+ }
+ strcpy (new->filename, arg);
+
+ data = read_line (stdin);
+ if (data == NULL)
+ {
+ pending_error_text = malloc (80 + strlen (arg));
+ if (pending_error_text)
+ {
+ if (feof (stdin))
+ sprintf (pending_error_text,
+ "E end of file reading mode for %s", arg);
+ else
+ {
+ sprintf (pending_error_text,
+ "E error reading mode for %s", arg);
+ pending_error = errno;
+ }
+ }
+ else
+ pending_error = ENOMEM;
+ }
+ else if (data == NO_MEM_ERROR)
+ {
+ pending_error = ENOMEM;
+ }
+ else
+ {
+ char *cp;
+
+ new->type = data;
+ if (data[1] != '\t')
+ goto error;
+ data[1] = '\0';
+ cp = data + 2;
+ new->val = cp;
+ cp = strchr (cp, '\t');
+ if (cp == NULL)
+ goto error;
+ *cp++ = '+';
+ cp = strchr (cp, '\t');
+ if (cp == NULL)
+ goto error;
+ *cp++ = '+';
+ cp = strchr (cp, '\t');
+ if (cp == NULL)
+ goto error;
+ *cp++ = '\0';
+ new->watches = cp;
+ /* If there is another tab, ignore everything after it,
+ for future expansion. */
+ cp = strchr (cp, '\t');
+ if (cp != NULL)
+ {
+ *cp = '\0';
+ }
+
+ new->next = NULL;
+
+ if (last_node == NULL)
+ {
+ notify_list = new;
+ }
+ else
+ last_node->next = new;
+ last_node = new;
+ }
+ return;
+ error:
+ pending_error_text = malloc (40);
+ if (pending_error_text)
+ strcpy (pending_error_text,
+ "E Protocol error; misformed Notify request");
+ pending_error = 0;
+ return;
+}
+
+/* Process all the Notify requests that we have stored up. Returns 0
+ if successful, if not prints error message (via error()) and
+ returns negative value. */
+static int
+server_notify ()
+{
+ struct notify_note *p;
+ char *repos;
+ List *list;
+ Node *node;
+ int status;
+
+ while (notify_list != NULL)
+ {
+ if (chdir (notify_list->dir) < 0)
+ {
+ error (0, errno, "cannot change to %s", notify_list->dir);
+ return -1;
+ }
+ repos = Name_Repository (NULL, NULL);
+
+ /* Now writelock. */
+ list = getlist ();
+ node = getnode ();
+ node->type = LOCK;
+ node->key = xstrdup (repos);
+ status = addnode (list, node);
+ assert (status == 0);
+ Writer_Lock (list);
+
+ fileattr_startdir (repos);
+
+ notify_do (*notify_list->type, notify_list->filename, getcaller(),
+ notify_list->val, notify_list->watches, repos);
+
+ printf ("Notified ");
+ if (use_dir_and_repos)
+ {
+ char *dir = notify_list->dir + strlen (server_temp_dir) + 1;
+ if (dir[0] == '\0')
+ fputs (".", stdout);
+ else
+ fputs (dir, stdout);
+ fputs ("/\n", stdout);
+ }
+ fputs (repos, stdout);
+ fputs ("/", stdout);
+ fputs (notify_list->filename, stdout);
+ fputs ("\n", stdout);
+
+ p = notify_list->next;
+ free (notify_list->filename);
+ free (notify_list->dir);
+ free (notify_list->type);
+ free (notify_list);
+ notify_list = p;
+
+ fileattr_write ();
+ fileattr_free ();
+
+ /* Remove the writelock. */
+ Lock_Cleanup ();
+ dellist (&list);
+ }
+ /* do_cvs_command writes to stdout via write(), not stdio, so better
+ flush out the buffer. */
+ fflush (stdout);
+ return 0;
+}
+\f
static int argument_count;
static char **argument_vector;
static int argument_vector_size;
/* The size we allocate for each buffer_data structure. */
#define BUFFER_DATA_SIZE (4096)
+#ifdef SERVER_FLOWCONTROL
+/* The maximum we'll queue to the remote client before blocking. */
+# ifndef SERVER_HI_WATER
+# define SERVER_HI_WATER (2 * 1024 * 1024)
+# endif /* SERVER_HI_WATER */
+/* When the buffer drops to this, we restart the child */
+# ifndef SERVER_LO_WATER
+# define SERVER_LO_WATER (1 * 1024 * 1024)
+# endif /* SERVER_LO_WATER */
+#endif /* SERVER_FLOWCONTROL */
+
/* Linked list of available buffer_data structures. */
static struct buffer_data *free_buffer_data;
static void buf_output0 PROTO((struct buffer *, const char *));
static inline void buf_append_char PROTO((struct buffer *, int));
static int buf_send_output PROTO((struct buffer *));
-static int buf_len PROTO((struct buffer *));
static int set_nonblock PROTO((struct buffer *));
static int set_block PROTO((struct buffer *));
static int buf_send_counted PROTO((struct buffer *));
static void buf_copy_lines PROTO((struct buffer *, struct buffer *, int));
static int buf_copy_counted PROTO((struct buffer *, struct buffer *));
+#ifdef SERVER_FLOWCONTROL
+static int buf_count_mem PROTO((struct buffer *));
+static int set_nonblock_fd PROTO((int));
+#endif /* SERVER_FLOWCONTROL */
+
/* Allocate more buffer_data structures. */
static void
return 1;
}
+#ifdef SERVER_FLOWCONTROL
+/*
+ * Count how much data is stored in the buffer..
+ * Note that each buffer is a malloc'ed chunk BUFFER_DATA_SIZE.
+ */
+
+static int
+buf_count_mem (buf)
+ struct buffer *buf;
+{
+ struct buffer_data *data;
+ int mem = 0;
+
+ for (data = buf->data; data != NULL; data = data->next)
+ mem += BUFFER_DATA_SIZE;
+
+ return mem;
+}
+#endif /* SERVER_FLOWCONTROL */
+
/* Add data DATA of length LEN to BUF. */
static void
}
}
-/*
- * Count how many bytes are in the buffer
- */
-
-static int
-buf_len (buf)
- struct buffer *buf;
-{
- struct buffer_data *data;
- int count = 0;
-
- if (! buf->output)
- abort ();
-
- for (data = buf->data; data; data = data->next)
- {
- count += data->size;
- }
-
- return (count);
-}
-
-
/*
* Send all the output we've been saving up. Returns 0 for success or
* errno code. If the buffer has been set to be nonblocking, this
return 0;
}
+#ifdef SERVER_FLOWCONTROL
/*
* Set buffer BUF to non-blocking I/O. Returns 0 for success or errno
* code.
*/
+static int
+set_nonblock_fd (fd)
+ int fd;
+{
+ int flags;
+
+ flags = fcntl (fd, F_GETFL, 0);
+ if (flags < 0)
+ return errno;
+ if (fcntl (fd, F_SETFL, flags | O_NONBLOCK) < 0)
+ return errno;
+ return 0;
+}
+#endif /* SERVER_FLOWCONTROL */
+
static int
set_nonblock (buf)
struct buffer *buf;
/*NOTREACHED*/
}
\f
+/* While processing requests, this buffer accumulates data to be sent to
+ the client, and then once we are in do_cvs_command, we use it
+ for all the data to be sent. */
+static struct buffer buf_to_net;
+
+static void serve_questionable PROTO((char *));
+
+static void
+serve_questionable (arg)
+ char *arg;
+{
+ static int initted;
+
+ if (!initted)
+ {
+ /* Pick up ignores from CVSROOTADM_IGNORE, $HOME/.cvsignore on server,
+ and CVSIGNORE on server. */
+ ign_setup ();
+ initted = 1;
+ }
+
+ if (dir_name == NULL)
+ {
+ buf_output0 (&buf_to_net, "E Protocol error: 'Directory' missing");
+ return;
+ }
+
+ if (!ign_name (arg))
+ {
+ char *update_dir;
+
+ buf_output (&buf_to_net, "M ? ", 4);
+ update_dir = dir_name + strlen (server_temp_dir) + 1;
+ if (!(update_dir[0] == '.' && update_dir[1] == '\0'))
+ {
+ buf_output0 (&buf_to_net, update_dir);
+ buf_output (&buf_to_net, "/", 1);
+ }
+ buf_output0 (&buf_to_net, arg);
+ buf_output (&buf_to_net, "\n", 1);
+ }
+}
+\f
static struct buffer protocol;
static void
static struct fd_set_wrapper { fd_set fds; } command_fds_to_drain;
static int max_command_fd;
+#ifdef SERVER_FLOWCONTROL
+static int flowcontrol_pipe[2];
+#endif /* SERVER_FLOWCONTROL */
+
static void
do_cvs_command (command)
int (*command) PROTO((int argc, char **argv));
if (print_pending_error ())
goto free_args_and_return;
+ (void) server_notify ();
+
/*
* We use a child process which actually does the operation. This
* is so we can intercept its standard output. Even if all of CVS
print_error (errno);
goto error_exit;
}
+#ifdef SERVER_FLOWCONTROL
+ if (pipe (flowcontrol_pipe) < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ set_nonblock_fd (flowcontrol_pipe[0]);
+ set_nonblock_fd (flowcontrol_pipe[1]);
+#endif /* SERVER_FLOWCONTROL */
dev_null_fd = open ("/dev/null", O_RDONLY);
if (dev_null_fd < 0)
close (stdout_pipe[0]);
close (stderr_pipe[0]);
close (protocol_pipe[0]);
+#ifdef SERVER_FLOWCONTROL
+ close (flowcontrol_pipe[1]);
+#endif /* SERVER_FLOWCONTROL */
/*
* Set this in .bashrc if you want to give yourself time to attach
/* OK, sit around getting all the input from the child. */
{
- struct buffer outbuf;
struct buffer stdoutbuf;
struct buffer stderrbuf;
struct buffer protocol_inbuf;
/* Number of file descriptors to check in select (). */
int num_to_check;
int count_needed = 0;
+#ifdef SERVER_FLOWCONTROL
+ int have_flowcontrolled = 0;
+#endif /* SERVER_FLOWCONTROL */
FD_ZERO (&command_fds_to_drain.fds);
num_to_check = stdout_pipe[0];
goto error_exit;
}
- outbuf.data = outbuf.last = NULL;
- outbuf.fd = STDOUT_FILENO;
- outbuf.output = 1;
- outbuf.nonblocking = 0;
- outbuf.memory_error = outbuf_memory_error;
-
stdoutbuf.data = stdoutbuf.last = NULL;
stdoutbuf.fd = stdout_pipe[0];
stdoutbuf.output = 0;
protocol_inbuf.nonblocking = 0;
protocol_inbuf.memory_error = input_memory_error;
- set_nonblock (&outbuf);
+ set_nonblock (&buf_to_net);
set_nonblock (&stdoutbuf);
set_nonblock (&stderrbuf);
set_nonblock (&protocol_inbuf);
}
protocol_pipe[1] = -1;
+#ifdef SERVER_FLOWCONTROL
+ if (close (flowcontrol_pipe[0]) < 0)
+ {
+ print_error (errno);
+ goto error_exit;
+ }
+ flowcontrol_pipe[0] = -1;
+#endif /* SERVER_FLOWCONTROL */
+
if (close (dev_null_fd) < 0)
{
print_error (errno);
fd_set readfds;
fd_set writefds;
int numfds;
+#ifdef SERVER_FLOWCONTROL
+ int bufmemsize;
+
+ /*
+ * See if we are swamping the remote client and filling our VM.
+ * Tell child to hold off if we do.
+ */
+ bufmemsize = buf_count_mem (&buf_to_net);
+ if (!have_flowcontrolled && (bufmemsize > SERVER_HI_WATER))
+ {
+ if (write(flowcontrol_pipe[1], "S", 1) == 1)
+ have_flowcontrolled = 1;
+ }
+ else if (have_flowcontrolled && (bufmemsize < SERVER_LO_WATER))
+ {
+ if (write(flowcontrol_pipe[1], "G", 1) == 1)
+ have_flowcontrolled = 0;
+ }
+#endif /* SERVER_FLOWCONTROL */
FD_ZERO (&readfds);
FD_ZERO (&writefds);
- if (! buf_empty_p (&outbuf))
- FD_SET (STDOUT_FILENO, &writefds);
- if ( buf_len (&outbuf) < 2*1024*1024) {
- if (stdout_pipe[0] >= 0)
- {
- FD_SET (stdout_pipe[0], &readfds);
- }
- if (stderr_pipe[0] >= 0)
- {
- FD_SET (stderr_pipe[0], &readfds);
- }
- if (protocol_pipe[0] >= 0)
- {
- FD_SET (protocol_pipe[0], &readfds);
- }
+ if (! buf_empty_p (&buf_to_net))
+ FD_SET (STDOUT_FILENO, &writefds);
+
+ if (stdout_pipe[0] >= 0)
+ {
+ FD_SET (stdout_pipe[0], &readfds);
+ }
+ if (stderr_pipe[0] >= 0)
+ {
+ FD_SET (stderr_pipe[0], &readfds);
+ }
+ if (protocol_pipe[0] >= 0)
+ {
+ FD_SET (protocol_pipe[0], &readfds);
}
do {
if (FD_ISSET (STDOUT_FILENO, &writefds))
{
/* What should we do with errors? syslog() them? */
- buf_send_output (&outbuf);
+ buf_send_output (&buf_to_net);
}
if (stdout_pipe[0] >= 0
status = buf_input_data (&stdoutbuf, (int *) NULL);
- buf_copy_lines (&outbuf, &stdoutbuf, 'M');
+ buf_copy_lines (&buf_to_net, &stdoutbuf, 'M');
if (status == -1)
stdout_pipe[0] = -1;
}
/* What should we do with errors? syslog() them? */
- buf_send_output (&outbuf);
+ buf_send_output (&buf_to_net);
}
if (stderr_pipe[0] >= 0
status = buf_input_data (&stderrbuf, (int *) NULL);
- buf_copy_lines (&outbuf, &stderrbuf, 'E');
+ buf_copy_lines (&buf_to_net, &stderrbuf, 'E');
if (status == -1)
stderr_pipe[0] = -1;
}
/* What should we do with errors? syslog() them? */
- buf_send_output (&outbuf);
+ buf_send_output (&buf_to_net);
}
if (protocol_pipe[0] >= 0
*/
count_needed -= count_read;
if (count_needed <= 0)
- count_needed = buf_copy_counted (&outbuf, &protocol_inbuf);
+ count_needed = buf_copy_counted (&buf_to_net,
+ &protocol_inbuf);
if (status == -1)
protocol_pipe[0] = -1;
}
/* What should we do with errors? syslog() them? */
- buf_send_output (&outbuf);
+ buf_send_output (&buf_to_net);
}
}
if (! buf_empty_p (&stdoutbuf))
{
buf_append_char (&stdoutbuf, '\n');
- buf_copy_lines (&outbuf, &stdoutbuf, 'M');
+ buf_copy_lines (&buf_to_net, &stdoutbuf, 'M');
}
if (! buf_empty_p (&stderrbuf))
{
buf_append_char (&stderrbuf, '\n');
- buf_copy_lines (&outbuf, &stderrbuf, 'E');
+ buf_copy_lines (&buf_to_net, &stderrbuf, 'E');
}
if (! buf_empty_p (&protocol_inbuf))
- buf_output0 (&outbuf,
+ buf_output0 (&buf_to_net,
"E Protocol error: uncounted data discarded\n");
errs = 0;
* OK, we've waited for the child. By now all CVS locks are free
* and it's OK to block on the network.
*/
- set_block (&outbuf);
- buf_send_output (&outbuf);
+ set_block (&buf_to_net);
+ buf_send_output (&buf_to_net);
}
if (errs)
return;
}
\f
+#ifdef SERVER_FLOWCONTROL
+/*
+ * Called by the child at convenient points in the server's execution for
+ * the server child to block.. ie: when it has no locks active.
+ */
+void
+server_pause_check()
+{
+ int paused = 0;
+ char buf[1];
+
+ while (read (flowcontrol_pipe[0], buf, 1) == 1)
+ {
+ if (*buf == 'S') /* Stop */
+ paused = 1;
+ else if (*buf == 'G') /* Go */
+ paused = 0;
+ else
+ return; /* ??? */
+ }
+ while (paused) {
+ int numfds, numtocheck;
+ fd_set fds;
+
+ FD_ZERO (&fds);
+ FD_SET (flowcontrol_pipe[0], &fds);
+ numtocheck = flowcontrol_pipe[0] + 1;
+
+ do {
+ numfds = select (numtocheck, &fds, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)NULL);
+ if (numfds < 0
+ && errno != EINTR)
+ {
+ print_error (errno);
+ return;
+ }
+ } while (numfds < 0);
+
+ if (FD_ISSET (flowcontrol_pipe[0], &fds))
+ {
+ while (read (flowcontrol_pipe[0], buf, 1) == 1)
+ {
+ if (*buf == 'S') /* Stop */
+ paused = 1;
+ else if (*buf == 'G') /* Go */
+ paused = 0;
+ else
+ return; /* ??? */
+ }
+ }
+ }
+}
+#endif /* SERVER_FLOWCONTROL */
+\f
static void output_dir PROTO((char *, char *));
static void
{
int len;
+ if (options == NULL)
+ options = "";
+
if (trace)
{
(void) fprintf (stderr,
"%c-> server_register(%s, %s, %s, %s, %s, %s, %s)\n",
(server_active) ? 'S' : ' ', /* silly */
- name, version, timestamp, options, tag,
- date, conflict);
+ name, version, timestamp, options, tag ? tag : "",
+ date ? date : "", conflict ? conflict : "");
}
- if (options == NULL)
- options = "";
-
if (entries_line != NULL)
{
/*
do_cvs_command (commit);
}
+static void
+checked_in_response (file, update_dir, repository)
+ char *file;
+ char *update_dir;
+ char *repository;
+{
+ if (supported_response ("Mode"))
+ {
+ struct stat sb;
+ char *mode_string;
+
+ if (stat (file, &sb) < 0)
+ {
+ /* Not clear to me why the file would fail to exist, but it
+ was happening somewhere in the testsuite. */
+ if (!existence_error (errno))
+ error (0, errno, "cannot stat %s", file);
+ }
+ else
+ {
+ buf_output0 (&protocol, "Mode ");
+ mode_string = mode_to_string (sb.st_mode);
+ buf_output0 (&protocol, mode_string);
+ buf_output0 (&protocol, "\n");
+ free (mode_string);
+ }
+ }
+
+ buf_output0 (&protocol, "Checked-in ");
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, file);
+ buf_output (&protocol, "\n", 1);
+ new_entries_line ();
+}
+
void
server_checked_in (file, update_dir, repository)
char *file;
}
else
{
- buf_output0 (&protocol, "Checked-in ");
- output_dir (update_dir, repository);
- buf_output0 (&protocol, file);
- buf_output (&protocol, "\n", 1);
- new_entries_line ();
+ checked_in_response (file, update_dir, repository);
}
buf_send_counted (&protocol);
}
if (noexec)
return;
if (updated == SERVER_UPDATED)
- buf_output0 (&protocol, "Checked-in ");
+ checked_in_response (file, update_dir, repository);
else
{
if (!supported_response ("New-entry"))
return;
buf_output0 (&protocol, "New-entry ");
+ output_dir (update_dir, repository);
+ buf_output0 (&protocol, file);
+ buf_output (&protocol, "\n", 1);
+ new_entries_line ();
}
- output_dir (update_dir, repository);
- buf_output0 (&protocol, file);
- buf_output (&protocol, "\n", 1);
- new_entries_line ();
buf_send_counted (&protocol);
}
\f
do_cvs_command (release);
}
+static void serve_watch_on PROTO ((char *));
+
+static void
+serve_watch_on (arg)
+ char *arg;
+{
+ do_cvs_command (watch_on);
+}
+
+static void serve_watch_off PROTO ((char *));
+
+static void
+serve_watch_off (arg)
+ char *arg;
+{
+ do_cvs_command (watch_off);
+}
+
+static void serve_watch_add PROTO ((char *));
+
+static void
+serve_watch_add (arg)
+ char *arg;
+{
+ do_cvs_command (watch_add);
+}
+
+static void serve_watch_remove PROTO ((char *));
+
+static void
+serve_watch_remove (arg)
+ char *arg;
+{
+ do_cvs_command (watch_remove);
+}
+
+static void serve_watchers PROTO ((char *));
+
+static void
+serve_watchers (arg)
+ char *arg;
+{
+ do_cvs_command (watchers);
+}
+
+static void serve_editors PROTO ((char *));
+
+static void
+serve_editors (arg)
+ char *arg;
+{
+ do_cvs_command (editors);
+}
+
+static int noop PROTO ((int, char **));
+
+static int
+noop (argc, argv)
+ int argc;
+ char **argv;
+{
+ return 0;
+}
+
+static void serve_noop PROTO ((char *));
+
+static void
+serve_noop (arg)
+ char *arg;
+{
+ do_cvs_command (noop);
+}
+\f
static void
serve_co (arg)
char *arg;
if (stat (file, &sb) < 0)
{
- if (errno == ENOENT)
+ if (existence_error (errno))
{
/*
* If we have a sticky tag for a branch on which the
expansion. */
if (mwhere != NULL)
- printf ("Module-expansion %s\n", mwhere);
+ {
+ printf ("Module-expansion %s", mwhere);
+ if (mfile != NULL)
+ {
+ printf ("/%s", mfile);
+ }
+ printf ("\n");
+ }
else
{
/* We may not need to do this anymore -- check the definition
REQ_LINE("Lost", serve_lost, rq_optional),
REQ_LINE("UseUnchanged", serve_enable_unchanged, rq_enableme),
REQ_LINE("Unchanged", serve_unchanged, rq_optional),
+ REQ_LINE("Notify", serve_notify, rq_optional),
+ REQ_LINE("Questionable", serve_questionable, rq_optional),
REQ_LINE("Argument", serve_argument, rq_essential),
REQ_LINE("Argumentx", serve_argumentx, rq_essential),
REQ_LINE("Global_option", serve_global_option, rq_optional),
REQ_LINE("export", serve_export, rq_optional),
REQ_LINE("history", serve_history, rq_optional),
REQ_LINE("release", serve_release, rq_optional),
+ REQ_LINE("watch-on", serve_watch_on, rq_optional),
+ REQ_LINE("watch-off", serve_watch_off, rq_optional),
+ REQ_LINE("watch-add", serve_watch_add, rq_optional),
+ REQ_LINE("watch-remove", serve_watch_remove, rq_optional),
+ REQ_LINE("watchers", serve_watchers, rq_optional),
+ REQ_LINE("editors", serve_editors, rq_optional),
+ REQ_LINE("noop", serve_noop, rq_optional),
REQ_LINE(NULL, NULL, rq_optional)
#undef REQ_LINE
printf ("\nok\n");
}
+#ifdef sun
/*
* Delete temporary files. SIG is the signal making this happen, or
* 0 if not called as a result of a signal.
if (r == command_pid)
command_pid_is_dead++;
}
+#endif
void
server_cleanup (sig)
argument_count = 1;
argument_vector[0] = "Dummy argument 0";
+ buf_to_net.data = buf_to_net.last = NULL;
+ buf_to_net.fd = STDOUT_FILENO;
+ buf_to_net.output = 1;
+ buf_to_net.nonblocking = 0;
+ buf_to_net.memory_error = outbuf_memory_error;
+
server_active = 1;
+
while (1)
{
char *cmd, *orig_cmd;
return 0;
}
+\f
+#ifdef AUTH_SERVER_SUPPORT
+
+/* This was test code, which we may need again. */
+#if 0
+ /* If we were invoked this way, then stdin comes from the
+ client and stdout/stderr writes to it. */
+ int c;
+ while ((c = getc (stdin)) != EOF && c != '*')
+ {
+ printf ("%c", toupper (c));
+ fflush (stdout);
+ }
+ exit (0);
+#endif /* 1/0 */
+
+\f
+/*
+ * 0 means no entry found for this user.
+ * 1 means entry found and password matches.
+ * 2 means entry found, but password does not match.
+ */
+int
+check_repository_password (username, password, repository)
+ char *username, *password, *repository;
+{
+ int retval = 0;
+ FILE *fp;
+ char *filename;
+ char *linebuf;
+ int ch;
+ int found_it = 0, namelen, linelen;
+
+ filename = xmalloc (strlen (repository)
+ + 1
+ + strlen ("CVSROOT")
+ + 1
+ + strlen ("passwd")
+ + 1);
+
+ strcpy (filename, repository);
+ strcat (filename, "/CVSROOT");
+ strcat (filename, "/passwd");
+
+ /* 32 is enough to cover the hashed password. I don't know if this
+ * counts as an arbitrary limit or not; it really depends on how
+ * standardized crypt() is.
+ * Answer: FreeBSD and Debian have played with the idea of making
+ * crypt() do MD5 which has a longer value; it would better not to
+ * make assumptions. So yes, FIXME: arbitrary limit.
+ */
+
+ /* USERNAME : PASSWD \n \0 */
+ linelen = strlen (username) + 1 + 32 + 1 + 1;
+ linebuf = xmalloc (linelen);
+ memset (linebuf, 0, linelen);
+
+ fp = fopen (filename, "r");
+ if (fp == NULL)
+ {
+ if (!existence_error (errno))
+ error (0, errno, "cannot open %s", filename);
+ return 0;
+ }
+
+ /* Look for a relevant line -- one with this user's name. */
+ namelen = strlen (username);
+ while (fgets (linebuf, linelen, fp))
+ {
+ if ((strncmp (linebuf, username, namelen) == 0)
+ && (linebuf[namelen] == ':'))
+ {
+ found_it = 1;
+ break;
+ }
+ else if (! strchr (linebuf, '\n'))
+ {
+ while ((ch = getc (fp)) != '\n')
+ if (ch == EOF)
+ break;
+ }
+ }
+ if (ferror (fp))
+ error (0, errno, "cannot read %s", filename);
+ if (fclose (fp) < 0)
+ error (0, errno, "cannot close %s", filename);
+
+ /* If found_it != 0, then linebuf contains the information we need. */
+ if (found_it)
+ {
+ char *found_password;
+
+ strtok (linebuf, ":");
+ found_password = strtok (NULL, ": \n");
+
+ if (strcmp (found_password, crypt (password, found_password)) == 0)
+ retval = 1;
+ else
+ retval = 2;
+ }
+ else
+ retval = 0;
+
+ free (filename);
+
+ return retval;
+}
+
+
+/* Return 1 if password matches, else 0. */
+int
+check_password (username, password, repository)
+ char *username, *password, *repository;
+{
+ int rc;
+
+ /* First we see if this user has a password in the CVS-specific
+ password file. If so, that's enough to authenticate with. If
+ not, we'll check /etc/passwd. */
+
+ rc = check_repository_password (username, password, repository);
+
+ if (rc == 1)
+ return 1;
+ else if (rc == 2)
+ return 0;
+ else if (rc == 0)
+ {
+ /* No cvs password found, so try /etc/passwd. */
+
+ struct passwd *pw;
+ char *found_passwd;
+
+ pw = getpwnam (username);
+ if (pw == NULL)
+ {
+ printf ("E Fatal error, aborting.\n"
+ "error 0 %s: no such user\n", username);
+ exit (1);
+ }
+ found_passwd = pw->pw_passwd;
+
+ if (found_passwd && *found_passwd)
+ return (! strcmp (found_passwd, crypt (password, found_passwd)));
+ else if (password && *password)
+ return 1;
+ else
+ return 0;
+ }
+ else
+ {
+ /* Something strange happened. We don't know what it was, but
+ we certainly won't grant authorization. */
+ return 0;
+ }
+}
+
+
+/* Read username and password from client (i.e., stdin).
+ If correct, then switch to run as that user and send an ACK to the
+ client via stdout, else send NACK and die. */
+void
+authenticate_connection ()
+{
+ int len;
+ char tmp[PATH_MAX];
+ char repository[PATH_MAX];
+ char username[PATH_MAX];
+ char password[PATH_MAX];
+ char *descrambled_password;
+ char server_user[PATH_MAX];
+ struct passwd *pw;
+ int verify_and_exit = 0;
+
+ /* The Authentication Protocol. Client sends:
+ *
+ * BEGIN AUTH REQUEST\n
+ * <REPOSITORY>\n
+ * <USERNAME>\n
+ * <PASSWORD>\n
+ * END AUTH REQUEST\n
+ *
+ * Server uses above information to authenticate, then sends
+ *
+ * I LOVE YOU\n
+ *
+ * if it grants access, else
+ *
+ * I HATE YOU\n
+ *
+ * if it denies access (and it exits if denying).
+ *
+ * When the client is "cvs login", the user does not desire actual
+ * repository access, but would like to confirm the password with
+ * the server. In this case, the start and stop strings are
+ *
+ * BEGIN VERIFICATION REQUEST\n
+ *
+ * and
+ *
+ * END VERIFICATION REQUEST\n
+ *
+ * On a verification request, the server's responses are the same
+ * (with the obvious semantics), but it exits immediately after
+ * sending the response in both cases.
+ *
+ * Why is the repository sent? Well, note that the actual
+ * client/server protocol can't start up until authentication is
+ * successful. But in order to perform authentication, the server
+ * needs to look up the password in the special CVS passwd file,
+ * before trying /etc/passwd. So the client transmits the
+ * repository as part of the "authentication protocol". The
+ * repository will be redundantly retransmitted later, but that's no
+ * big deal.
+ */
+
+ /* Since we're in the server parent process, error should use the
+ protocol to report error messages. */
+ error_use_protocol = 1;
+
+ /* Make sure the protocol starts off on the right foot... */
+ fgets (tmp, PATH_MAX, stdin);
+ if (strcmp (tmp, "BEGIN VERIFICATION REQUEST\n") == 0)
+ verify_and_exit = 1;
+ else if (strcmp (tmp, "BEGIN AUTH REQUEST\n") != 0)
+ error (1, 0, "bad auth protocol start: %s", tmp);
+
+ /* Get the three important pieces of information in order. */
+ fgets (repository, PATH_MAX, stdin);
+ fgets (username, PATH_MAX, stdin);
+ fgets (password, PATH_MAX, stdin);
+
+ /* Make them pure. */
+ strip_trailing_newlines (repository);
+ strip_trailing_newlines (username);
+ strip_trailing_newlines (password);
+
+ /* ... and make sure the protocol ends on the right foot. */
+ fgets (tmp, PATH_MAX, stdin);
+ if (strcmp (tmp,
+ verify_and_exit ?
+ "END VERIFICATION REQUEST\n" : "END AUTH REQUEST\n")
+ != 0)
+ {
+ error (1, 0, "bad auth protocol end: %s", tmp);
+ }
+
+ /* We need the real cleartext before we hash it. */
+ descrambled_password = descramble (password);
+
+ if (check_password (username, descrambled_password, repository))
+ {
+ printf ("I LOVE YOU\n");
+ fflush (stdout);
+ memset (descrambled_password, 0, strlen (descrambled_password));
+ free (descrambled_password);
+ }
+ else
+ {
+ printf ("I HATE YOU\n");
+ fflush (stdout);
+ memset (descrambled_password, 0, strlen (descrambled_password));
+ free (descrambled_password);
+ exit (1);
+ }
+
+ /* Don't go any farther if we're just responding to "cvs login". */
+ if (verify_and_exit)
+ exit (0);
+
+ /* Switch to run as this user. */
+ pw = getpwnam (username);
+ if (pw == NULL)
+ {
+ error (1, 0,
+ "fatal error, aborting.\nerror 0 %s: no such user\n",
+ username);
+ }
+
+ initgroups (pw->pw_name, pw->pw_gid);
+ setgid (pw->pw_gid);
+ setuid (pw->pw_uid);
+ /* Inhibit access by randoms. Don't want people randomly
+ changing our temporary tree before we check things in. */
+ umask (077);
+
+#if HAVE_PUTENV
+ /* Set LOGNAME and USER in the environment, in case they are
+ already set to something else. */
+ {
+ char *env;
+
+ env = xmalloc (sizeof "LOGNAME=" + strlen (username));
+ (void) sprintf (env, "LOGNAME=%s", username);
+ (void) putenv (env);
+
+ env = xmalloc (sizeof "USER=" + strlen (username));
+ (void) sprintf (env, "USER=%s", username);
+ (void) putenv (env);
+ }
+#endif /* HAVE_PUTENV */
+}
+
+#endif /* AUTH_SERVER_SUPPORT */
+
+
#endif /* SERVER_SUPPORT */
+