Merge changes from CYCLIC branch onto mainline. rcs.[ch] still to come.

This commit is contained in:
peter 1998-01-26 03:24:11 +00:00
parent b309b273f1
commit f163ab844b
11 changed files with 1934 additions and 912 deletions

View File

@ -3,7 +3,7 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS 1.4 kit.
* specified in the README file that comes with the CVS source distribution.
*
* Commit Files
*
@ -92,6 +92,7 @@ static const char *const commit_usage[] =
"\t-F file\tRead the log message from file.\n",
"\t-m msg\tLog message.\n",
"\t-r rev\tCommit to this branch or trunk revision.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
@ -144,8 +145,22 @@ find_dirent_proc (callerdat, dir, repository, update_dir, entries)
{
struct find_data *find_data = (struct find_data *)callerdat;
/* This check seems to slowly be creeping throughout CVS (update
and send_dirent_proc by CVS 1.5, diff in 31 Oct 1995. My guess
is that it (or some variant thereof) should go in all the
dirent procs. Unless someone has some better idea... */
if (!isdir (dir))
return (R_SKIP_ALL);
/* initialize the ignore list for this directory */
find_data->ignlist = getlist ();
/* Print the same warm fuzzy as in check_direntproc, since that
code will never be run during client/server operation and we
want the messages to match. */
if (!quiet)
error (0, 0, "Examining %s", update_dir);
return R_PROCESS;
}
@ -232,7 +247,7 @@ find_fileproc (callerdat, finfo)
xfinfo.repository = NULL;
xfinfo.rcs = NULL;
vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0);
vers = Version_TS (&xfinfo, NULL, tag, NULL, 0, 0);
if (vers->ts_user == NULL
&& vers->vn_user != NULL
&& vers->vn_user[0] == '-')
@ -246,8 +261,8 @@ find_fileproc (callerdat, finfo)
if (vers->ts_user == NULL)
error (0, 0, "nothing known about `%s'", finfo->fullname);
else
error (0, 0, "use `cvs add' to create an entry for %s",
finfo->fullname);
error (0, 0, "use `%s add' to create an entry for %s",
program_name, finfo->fullname);
return 1;
}
else if (vers->ts_user != NULL
@ -321,6 +336,9 @@ commit (argc, argv)
* For log purposes, do not allow "root" to commit files. If you look
* like root, but are really logged in as a non-root user, it's OK.
*/
/* FIXME: Shouldn't this check be much more closely related to the
readonly user stuff (CVSROOT/readers, &c). That is, why should
root be able to "cvs init", "cvs import", &c, but not "cvs ci"? */
if (geteuid () == (uid_t) 0)
{
struct passwd *pw;
@ -342,9 +360,9 @@ commit (argc, argv)
break;
case 'm':
#ifdef FORCE_USE_EDITOR
use_editor = TRUE;
use_editor = 1;
#else
use_editor = FALSE;
use_editor = 0;
#endif
if (message)
{
@ -371,9 +389,9 @@ commit (argc, argv)
break;
case 'F':
#ifdef FORCE_USE_EDITOR
use_editor = TRUE;
use_editor = 1;
#else
use_editor = FALSE;
use_editor = 0;
#endif
logfile = optarg;
break;
@ -425,6 +443,7 @@ commit (argc, argv)
#ifdef CLIENT_SUPPORT
if (client_active)
{
int err;
struct find_data find_args;
ign_setup ();
@ -489,6 +508,8 @@ commit (argc, argv)
do_verify (&message, (char *)NULL);
/* We always send some sort of message, even if empty. */
/* FIXME: is that true? There seems to be some code in do_editor
which can leave the message NULL. */
option_with_arg ("-m", message);
/* OK, now process all the questionable files we have been saving
@ -553,14 +574,39 @@ commit (argc, argv)
"cvs commit -r 2" across a whole bunch of files a very slow
operation (and it isn't documented in cvsclient.texi). I
haven't looked at the server code carefully enough to be
_sure_ why this is needed, but if it is because RCS_CI
wants the file to exist, then it would be relatively simple
(but not trivial) to fix in the server. */
_sure_ why this is needed, but if it is because the "ci"
program, which we used to call, wanted the file to exist,
then it would be relatively simple to fix in the server. */
send_files (find_args.argc, find_args.argv, local, 0,
find_args.force ? SEND_FORCE : 0);
send_to_server ("ci\012", 0);
return get_responses_and_close ();
err = get_responses_and_close ();
if (err != 0 && use_editor && message != NULL)
{
/* If there was an error, don't nuke the user's carefully
constructed prose. This is something of a kludge; a better
solution is probably more along the lines of #150 in TODO
(doing a second up-to-date check before accepting the
log message has also been suggested, but that seems kind of
iffy because the real up-to-date check could still fail,
another error could occur, &c. Also, a second check would
slow things down). */
char *fname;
FILE *fp;
fname = cvs_temp_name ();
fp = CVS_FOPEN (fname, "w+");
if (fp == NULL)
error (1, 0, "cannot create temporary file %s", fname);
if (fwrite (message, 1, strlen (message), fp) != strlen (message))
error (1, errno, "cannot write temporary file %s", fname);
if (fclose (fp) < 0)
error (0, errno, "cannot close temporary file %s", fname);
error (0, 0, "saving log message in %s", fname);
}
return err;
}
#endif
@ -633,7 +679,10 @@ classify_file_internal (finfo, vers)
{
int save_noexec, save_quiet, save_really_quiet;
Ctype status;
/* FIXME: Do we need to save quiet as well as really_quiet? Last
time I glanced at Classify_File I only saw it looking at really_quiet
not quiet. */
save_noexec = noexec;
save_quiet = quiet;
save_really_quiet = really_quiet;
@ -817,16 +866,30 @@ check_fileproc (callerdat, finfo)
if (file_has_markers (finfo))
{
/* Make this a warning, not an error, because we have
no way of knowing whether the "conflict indicators"
are really from a conflict or whether they are part
of the document itself (cvs.texinfo and sanity.sh in
CVS itself, for example, tend to want to have strings
like ">>>>>>>" at the start of a line). Making people
kludge this the way they need to kludge keyword
expansion seems undesirable. And it is worse than
keyword expansion, because there is no -ko
analogue. */
error (0, 0,
"file `%s' still contains conflict indicators",
"\
warning: file `%s' seems to still contain conflict indicators",
finfo->fullname);
freevers_ts (&vers);
return (1);
}
}
if (status == T_REMOVED && vers->tag && isdigit (*vers->tag))
{
/* Remove also tries to forbid this, but we should check
here. I'm only _sure_ about somewhat obscure cases
(hacking the Entries file, using an old version of
CVS for the remove and a new one for the commit), but
there might be other cases. */
error (0, 0,
"cannot remove file `%s' which has a numeric sticky tag of `%s'",
finfo->fullname, vers->tag);
@ -946,7 +1009,8 @@ check_fileproc (callerdat, finfo)
}
/*
* Print warm fuzzies while examining the dirs
* By default, return the code that tells do_recursion to examine all
* directories
*/
/* ARGSUSED */
static Dtype
@ -957,6 +1021,9 @@ check_direntproc (callerdat, dir, repos, update_dir, entries)
char *update_dir;
List *entries;
{
if (!isdir (dir))
return (R_SKIP_ALL);
if (!quiet)
error (0, 0, "Examining %s", update_dir);
@ -1013,7 +1080,8 @@ precommit_proc (repository, filter)
free (s);
}
run_setup ("%s %s", filter, repository);
run_setup (filter);
run_arg (repository);
(void) walklist (ulist, precommit_list_proc, NULL);
return (run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_REALLY));
}
@ -1333,7 +1401,8 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries)
if (line[line_length - 1] == '\n')
line[--line_length] = '\0';
repository = Name_Repository ((char *) NULL, update_dir);
run_setup ("%s %s", line, repository);
run_setup (line);
run_arg (repository);
cvs_output (program_name, 0);
cvs_output (" ", 1);
cvs_output (command_name, 0);
@ -1365,7 +1434,7 @@ commit_filesdoneproc (callerdat, err, repository, update_dir, entries)
}
/*
* Get the log message for a dir and print a warm fuzzy
* Get the log message for a dir
*/
/* ARGSUSED */
static Dtype
@ -1380,6 +1449,9 @@ commit_direntproc (callerdat, dir, repos, update_dir, entries)
List *ulist;
char *real_repos;
if (!isdir (dir))
return (R_SKIP_ALL);
/* find the update list for this dir */
p = findnode (mulist, update_dir);
if (p != NULL)
@ -1391,10 +1463,6 @@ commit_direntproc (callerdat, dir, repos, update_dir, entries)
if (ulist == NULL || ulist->list->next == ulist->list)
return (R_SKIP_FILES);
/* print the warm fuzzy */
if (!quiet)
error (0, 0, "Committing %s", update_dir);
/* get commit message */
real_repos = Name_Repository (dir, update_dir);
got_message = 1;
@ -1489,10 +1557,10 @@ remove_file (finfo, tag, message)
error (1, 0, "internal error: no parsed RCS file");
branch = 0;
if (tag && !(branch = RCS_isbranch (finfo->rcs, tag)))
if (tag && !(branch = RCS_nodeisbranch (finfo->rcs, tag)))
{
/* a symbolic tag is specified; just remove the tag from the file */
if ((retcode = RCS_deltag (finfo->rcs, tag, 1)) != 0)
if ((retcode = RCS_deltag (finfo->rcs, tag)) != 0)
{
if (!quiet)
error (0, retcode == -1 ? errno : 0,
@ -1500,6 +1568,7 @@ remove_file (finfo, tag, message)
finfo->fullname);
return (1);
}
RCS_rewrite (finfo->rcs, NULL, NULL);
Scratch_Entry (finfo->entries, finfo->file);
return (0);
}
@ -1556,6 +1625,7 @@ remove_file (finfo, tag, message)
finfo->fullname);
return (1);
}
RCS_rewrite (finfo->rcs, NULL, NULL);
}
#ifdef SERVER_SUPPORT
@ -1584,12 +1654,15 @@ remove_file (finfo, tag, message)
/* Except when we are creating a branch, lock the revision so that
we can check in the new revision. */
if (lockflag)
RCS_lock (finfo->rcs, rev ? corev : NULL, 0);
{
if (RCS_lock (finfo->rcs, rev ? corev : NULL, 1) == 0)
RCS_rewrite (finfo->rcs, NULL, NULL);
}
if (corev != NULL)
free (corev);
retcode = RCS_checkin (finfo->rcs->path, finfo->file, message, rev,
retcode = RCS_checkin (finfo->rcs, finfo->file, message, rev,
RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
if (retcode != 0)
{
@ -1690,6 +1763,8 @@ unlockrcs (rcs)
if ((retcode = RCS_unlock (rcs, NULL, 0)) != 0)
error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
"could not unlock %s", rcs->path);
else
RCS_rewrite (rcs, NULL, NULL);
}
/*
@ -1730,6 +1805,7 @@ fixbranch (rcs, branch)
if ((retcode = RCS_setbranch (rcs, branch)) != 0)
error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
"cannot restore branch to %s for %s", branch, rcs->path);
RCS_rewrite (rcs, NULL, NULL);
}
}
@ -1841,29 +1917,63 @@ internal error: `%s' didn't move out of the attic",
{
/* this is the first time we have ever seen this file; create
an rcs file. */
run_setup ("%s%s -x,v/ -i", Rcsbin, RCS);
char *desc;
size_t descalloc;
size_t desclen;
char *opt;
desc = NULL;
descalloc = 0;
desclen = 0;
fname = xmalloc (strlen (file) + sizeof (CVSADM)
+ sizeof (CVSEXT_LOG) + 10);
(void) sprintf (fname, "%s/%s%s", CVSADM, file, CVSEXT_LOG);
/* If the file does not exist, no big deal. In particular, the
server does not (yet at least) create CVSEXT_LOG files. */
if (isfile (fname))
run_args ("-t%s/%s%s", CVSADM, file, CVSEXT_LOG);
/* FIXME: Should be including update_dir in the appropriate
place here. */
get_file (fname, fname, "r", &desc, &descalloc, &desclen);
free (fname);
/* From reading the RCS 5.7 source, "rcs -i" adds a newline to the
end of the log message if the message is nonempty.
Do it. RCS also deletes certain whitespace, in cleanlogmsg,
which we don't try to do here. */
if (desclen > 0)
{
expand_string (&desc, &descalloc, desclen + 1);
desc[desclen++] = '\012';
}
/* 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)
opt = options + 2;
else
opt = NULL;
/* This message is an artifact of the time when this
was implemented via "rcs -i". It should be revised at
some point (does the "initial revision" in the message from
RCS_checkin indicate that this is a new file? Or does the
"RCS file" message serve some function?). */
cvs_output ("RCS file: ", 0);
cvs_output (rcs, 0);
cvs_output ("\ndone\n", 0);
if (add_rcs_file (NULL, rcs, file, NULL, opt,
NULL, NULL, 0, NULL,
desc, desclen, NULL) != 0)
{
error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
"could not create %s", rcs);
retval = 1;
goto out;
}
rcsfile = RCS_parsercsfile (rcs);
newfile = 1;
if (desc != NULL)
free (desc);
}
/* when adding a file for the first time, and using a tag, we need
@ -1883,7 +1993,7 @@ internal error: `%s' didn't move out of the attic",
/* commit a dead revision. */
(void) sprintf (tmp, "file %s was initially added on branch %s.",
file, tag);
retcode = RCS_checkin (rcs, NULL, tmp, NULL,
retcode = RCS_checkin (rcsfile, NULL, tmp, NULL,
RCS_FLAGS_DEAD | RCS_FLAGS_QUIET);
free (tmp);
if (retcode != 0)
@ -1898,7 +2008,8 @@ internal error: `%s' didn't move out of the attic",
rename_file (fname, file);
free (fname);
assert (rcsfile == NULL);
/* double-check that the file was written correctly */
freercsnode (&rcsfile);
rcsfile = RCS_parse (file, repository);
if (rcsfile == NULL)
{
@ -1954,6 +2065,7 @@ internal error: `%s' didn't move out of the attic",
magicrev = RCS_magicrev (rcsfile, head);
retcode = RCS_settag (rcsfile, tag, magicrev);
RCS_rewrite (rcsfile, NULL, NULL);
free (head);
free (magicrev);
@ -1986,7 +2098,14 @@ internal error: `%s' didn't move out of the attic",
fileattr_newfile (file);
/* I don't think fix_rcs_modes is needed any more. In the
add_rcs_file case, the algorithms used by add_rcs_file and
fix_rcs_modes are the same, so there is no need to go through
it all twice. In the other cases, I think we want to just
preserve the mode that the file had before we started. That is
a behavior change, but I would think a desirable one. */
fix_rcs_modes (rcs, file);
retval = 0;
out:
@ -2033,12 +2152,13 @@ lock_RCS (user, rcs, rev, repository)
return (1);
}
}
err = RCS_lock(rcs, NULL, 0);
err = RCS_lock(rcs, NULL, 1);
}
else
{
(void) RCS_lock(rcs, rev, 1);
}
RCS_rewrite (rcs, NULL, NULL);
if (err == 0)
{

View File

@ -127,6 +127,8 @@ extern int errno;
#define CVSADM_NOTIFY "CVS/Notify."
#define CVSADM_NOTIFYTMP "CVS/Notify.tmp"
#define CVSADM_BASE "CVS/Base"
#define CVSADM_BASEREV "CVS/Baserev."
#define CVSADM_BASEREVTMP "CVS/Baserev.tmp"
#define CVSADM_TEMPLATE "CVS/Template."
#else /* USE_VMS_FILENAMES */
#define CVSADM "CVS"
@ -144,6 +146,8 @@ extern int errno;
/* A directory in which we store base versions of files we currently are
editing with "cvs edit". */
#define CVSADM_BASE "CVS/Base"
#define CVSADM_BASEREV "CVS/Baserev"
#define CVSADM_BASEREVTMP "CVS/Baserev.tmp"
/* File which contains the template for use in log messages. */
#define CVSADM_TEMPLATE "CVS/Template"
#endif /* USE_VMS_FILENAMES */
@ -182,6 +186,7 @@ extern int errno;
#define CVSROOTADM_READERS "readers"
#define CVSROOTADM_WRITERS "writers"
#define CVSROOTADM_PASSWD "passwd"
#define CVSROOTADM_CONFIG "config"
#define CVSROOTADM_OPTIONS "options"
#define CVSNULLREPOS "Emptydir" /* an empty directory */
@ -207,11 +212,10 @@ extern int errno;
/* Command attributes -- see function lookup_command_attribute(). */
#define CVS_CMD_IGNORE_ADMROOT 1
/* Set if CVS does _not_ need to create a CVS/Root file upon
completion of this command. The name is confusing, both because
the meaning is closer to "does not use working directory" than
"uses working directory" and because the flag isn't really as
general purpose as it seems (cvs release sets it). */
/* Set if CVS needs to create a CVS/Root file upon completion of this
command. The name may be slightly confusing, because the flag
isn't really as general purpose as it seems (it is not set for cvs
release). */
#define CVS_CMD_USES_WORK_DIR 2
@ -241,9 +245,6 @@ extern int errno;
#endif
#endif /* USE_VMS_FILENAMES */
#define FALSE 0
#define TRUE 1
/*
* Special tags. -rHEAD refers to the head of an RCS file, regardless of any
* sticky tags. -rBASE refers to the current revision the user has checked
@ -254,13 +255,10 @@ extern int errno;
/* Environment variable used by CVS */
#define CVSREAD_ENV "CVSREAD" /* make files read-only */
#define CVSREAD_DFLT FALSE /* writable files by default */
#define CVSREAD_DFLT 0 /* writable files by default */
#define CVSREADONLYFS_ENV "CVSREADONLYFS" /* repository is read-only */
#define RCSBIN_ENV "RCSBIN" /* RCS binary directory */
/* #define RCSBIN_DFLT Set by options.h */
#define TMPDIR_ENV "TMPDIR" /* Temporary directory */
/* #define TMPDIR_DFLT Set by options.h */
@ -303,8 +301,13 @@ struct entnode
enum ent_type type;
char *user;
char *version;
/* Timestamp, or "" if none (never NULL). */
char *timestamp;
/* Keyword expansion options, or "" if none (never NULL). */
char *options;
char *tag;
char *date;
char *conflict;
@ -356,7 +359,7 @@ typedef enum direnter_type Dtype;
#endif
extern char *program_name, *program_path, *command_name;
extern char *Rcsbin, *Tmpdir, *Editor;
extern char *Tmpdir, *Editor;
extern int cvsadmin_root;
extern char *CurDir;
extern int really_quiet, quiet;
@ -367,7 +370,8 @@ extern char *RCS_citag;
/* Access method specified in CVSroot. */
typedef enum {
local_method, server_method, pserver_method, kserver_method, ext_method
local_method, server_method, pserver_method, kserver_method, gserver_method,
ext_method
} CVSmethod;
extern char *method_names[]; /* change this in root.c if you change
the enum above */
@ -379,6 +383,8 @@ extern char *CVSroot_username; /* the username or NULL if method == local */
extern char *CVSroot_hostname; /* the hostname or NULL if method == local */
extern char *CVSroot_directory; /* the directory name */
extern char *emptydir_name PROTO ((void));
extern int trace; /* Show all commands */
extern int noexec; /* Don't modify disk anywhere */
extern int readonlyfs; /* fail on all write locks; succeed all read locks */
@ -394,20 +400,24 @@ extern char hostname[];
/* Externs that are included directly in the CVS sources */
int RCS_exec_settag PROTO((const char *, const char *, const char *));
int RCS_exec_deltag PROTO((const char *, const char *, int));
int RCS_exec_setbranch PROTO((const char *, const char *));
int RCS_exec_lock PROTO((const char *, const char *, int));
int RCS_exec_unlock PROTO((const char *, const char *, int));
int RCS_merge PROTO((const char *, const char *, const char *, const char *));
int RCS_merge PROTO((RCSNode *, char *, char *, char *, char *, char *));
/* Flags used by RCS_* functions. See the description of the individual
functions for which flags mean what for each function. */
#define RCS_FLAGS_FORCE 1
#define RCS_FLAGS_DEAD 2
#define RCS_FLAGS_QUIET 4
#define RCS_FLAGS_MODTIME 8
int RCS_checkin PROTO ((char *rcsfile, char *workfile, char *message,
char *rev, int flags));
extern int RCS_exec_rcsdiff PROTO ((RCSNode *rcsfile,
char *opts, char *options,
char *rev1, char *rev2,
char *label1, char *label2,
char *workfile));
extern int diff_exec PROTO ((char *file1, char *file2, char *options,
char *out));
extern int diff_execv PROTO ((char *file1, char *file2,
char *label1, char *label2,
char *options, char *out));
@ -421,9 +431,11 @@ List *Entries_Open PROTO((int aflag));
void Subdirs_Known PROTO((List *entries));
void Subdir_Register PROTO((List *, const char *, const char *));
void Subdir_Deregister PROTO((List *, const char *, const char *));
char *Make_Date PROTO((char *rawdate));
char *Name_Repository PROTO((char *dir, char *update_dir));
char *Short_Repository PROTO((char *repository));
void Sanitize_Repository_Name PROTO((char *repository));
char *Name_Root PROTO((char *dir, char *update_dir));
int parse_cvsroot PROTO((char *CVSroot));
@ -433,9 +445,8 @@ void root_allow_add PROTO ((char *));
void root_allow_free PROTO ((void));
int root_allow_ok PROTO ((char *));
int same_directories PROTO((char *dir1, char *dir2));
char *Short_Repository PROTO((char *repository));
char *gca PROTO((char *rev1, char *rev2));
char *gca PROTO((const char *rev1, const char *rev2));
extern void check_numeric PROTO ((const char *, int, char **));
char *getcaller PROTO((void));
char *time_stamp PROTO((char *file));
@ -448,6 +459,8 @@ int pathname_levels PROTO ((char *path));
typedef int (*CALLPROC) PROTO((char *repository, char *value));
int Parse_Info PROTO((char *infofile, char *repository, CALLPROC callproc, int all));
extern int parse_config PROTO ((char *));
typedef RETSIGTYPE (*SIGCLEANUPPROC) PROTO(());
int SIG_register PROTO((int sig, SIGCLEANUPPROC sigcleanup));
int isdir PROTO((const char *file));
@ -463,6 +476,8 @@ char *cvs_temp_name PROTO ((void));
void parseopts PROTO ((const char *root));
int numdots PROTO((const char *s));
char *increment_revnum PROTO ((const char *));
int compare_revnums PROTO ((const char *, const char *));
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));
@ -471,9 +486,10 @@ 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,
int nonbranch));
extern int Create_Admin PROTO ((char *dir, char *update_dir,
char *repository, char *tag, char *date,
int nonbranch, int warn));
extern int expand_at_signs PROTO ((char *, off_t, FILE *));
/* Locking subsystem (implemented in lock.c). */
@ -511,7 +527,7 @@ extern int ign_case;
#include "update.h"
void line2argv PROTO ((int *pargc, char ***argv, char *line));
void line2argv PROTO ((int *pargc, char ***argv, char *line, char *sepchars));
void make_directories PROTO((const char *name));
void make_directory PROTO((const char *name));
extern int mkdir_if_needed PROTO ((char *name));
@ -548,7 +564,7 @@ void do_editor PROTO((char *dir, char **messagep,
void do_verify PROTO((char **messagep, char *repository));
typedef int (*CALLBACKPROC) PROTO((int *pargc, char *argv[], char *where,
char *mwhere, char *mfile, int horten, int local_specified,
char *mwhere, char *mfile, int shorten, int local_specified,
char *omodule, char *msg));
/* This is the structure that the recursion processor passes to the
@ -609,7 +625,9 @@ void SIG_endCrSect PROTO((void));
void read_cvsrc PROTO((int *argc, char ***argv, char *cmdname));
char *make_message_rcslegal PROTO((char *message));
extern int file_has_markers PROTO ((struct file_info *));
extern int file_has_markers PROTO ((const struct file_info *));
extern void get_file PROTO ((const char *, const char *, const char *,
char **, size_t *, size_t *));
/* flags for run_exec(), the fast system() for CVS */
#define RUN_NORMAL 0x0000 /* no special behaviour */
@ -622,14 +640,9 @@ extern int file_has_markers PROTO ((struct file_info *));
void run_arg PROTO((const char *s));
void run_print PROTO((FILE * fp));
#ifdef HAVE_VPRINTF
void run_setup PROTO((const char *fmt,...));
void run_args PROTO((const char *fmt,...));
#else
void run_setup ();
void run_args ();
#endif
int run_exec PROTO((char *stin, char *stout, char *sterr, int flags));
void run_setup PROTO ((const char *prog));
int run_exec PROTO((const char *stin, const char *stout, const char *sterr,
int flags));
/* other similar-minded stuff from run.c. */
FILE *run_popen PROTO((const char *, const char *));
@ -649,10 +662,16 @@ pid_t waitpid PROTO((pid_t, int *, int));
struct vers_ts
{
/* 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. */
It can have the following special values:
NULL = file is not mentioned in Entries (this is also used for a
directory).
"" = ILLEGAL! The comment used to say that it meant "no user file"
but as far as I know CVS didn't actually use it that way.
Note that according to cvs.texinfo, "" is not legal in the
Entries file.
0 = user file is new
-vers = user file to be removed. */
char *vn_user;
/* Numeric revision number corresponding to ->vn_tag (->vn_tag
@ -675,7 +694,8 @@ struct vers_ts
and if they differ it is modified. */
char *ts_rcs;
/* Options from CVS/Entries (keyword expansion). */
/* Options from CVS/Entries (keyword expansion), malloc'd. If none,
then it is an empty string (never NULL). */
char *options;
/* If non-NULL, there was a conflict (or merely a merge? See merge_file)
@ -714,6 +734,11 @@ int Checkin PROTO ((int type, struct file_info *finfo, char *rcs, char *rev,
char *tag, char *options, char *message));
int No_Difference PROTO ((struct file_info *finfo, Vers_TS *vers));
/* CVSADM_BASEREV stuff, from entries.c. */
extern char *base_get PROTO ((struct file_info *));
extern void base_register PROTO ((struct file_info *, char *));
extern void base_deregister PROTO ((struct file_info *));
/*
* defines for Classify_File() to determine the current state of a file.
* These are also used as types in the data field for the list we make for
@ -824,10 +849,13 @@ extern void tag_check_valid PROTO ((char *, int, char **, int, int, char *));
extern void tag_check_valid_join PROTO ((char *, int, char **, int, int,
char *));
/* From server.c and documented there. */
extern void cvs_output PROTO ((const char *, size_t));
extern void cvs_output_binary PROTO ((char *, size_t));
extern void cvs_outerr PROTO ((const char *, size_t));
extern void cvs_flusherr PROTO ((void));
extern void cvs_flushout PROTO ((void));
extern void cvs_output_tagged PROTO ((char *, char *));
#if defined(SERVER_SUPPORT) || defined(CLIENT_SUPPORT)
#include "server.h"

View File

@ -3,7 +3,7 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS 1.4 kit.
* specified in the README file that comes with the CVS source distribution.
*
* Difference
*
@ -43,6 +43,7 @@ static void diff_mark_errors PROTO((int err));
static char *diff_rev1, *diff_rev2;
static char *diff_date1, *diff_date2;
static char *use_rev1, *use_rev2;
static int have_rev1_label, have_rev2_label;
/* Revision of the user file, if it is unchanged from something in the
repository and we want to use that fact. */
@ -71,6 +72,7 @@ static const char *const diff_usage[] =
"\t--ifdef=arg\tOutput diffs in ifdef format.\n",
"(consult the documentation for your diff program for rcsdiff-options.\n",
"The most popular is -c for context diffs but there are many more).\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
@ -103,13 +105,13 @@ static struct option const longopts[] =
{
{"ignore-blank-lines", 0, 0, 'B'},
{"context", 2, 0, 143},
{"ifdef", 1, 0, 147},
{"ifdef", 1, 0, 131},
{"show-function-line", 1, 0, 'F'},
{"speed-large-files", 0, 0, 'H'},
{"ignore-matching-lines", 1, 0, 'I'},
{"label", 1, 0, 'L'},
{"new-file", 0, 0, 'N'},
{"initial-tab", 0, 0, 'T'},
{"initial-tab", 0, 0, 148},
{"width", 1, 0, 'W'},
{"text", 0, 0, 'a'},
{"ignore-space-change", 0, 0, 'b'},
@ -130,7 +132,7 @@ static struct option const longopts[] =
{"report-identical-files", 0, 0, 's'},
{"expand-tabs", 0, 0, 't'},
{"ignore-all-space", 0, 0, 'w'},
{"side-by-side", 0, 0, 'y'},
{"side-by-side", 0, 0, 147},
{"unified", 2, 0, 146},
{"left-column", 0, 0, 129},
{"suppress-common-lines", 0, 0, 130},
@ -147,6 +149,37 @@ static struct option const longopts[] =
{0, 0, 0, 0}
};
/* CVS 1.9 and similar versions seemed to have pretty weird handling
of -y and -T. In the cases where it called rcsdiff,
they would have the meanings mentioned below. In the cases where it
called diff, they would have the meanings mentioned in "longopts".
Noone seems to have missed them, so I think the right thing to do is
just to remove the options altogether (which I have done).
In the case of -z and -q, "cvs diff" did not accept them even back
when we called rcsdiff (at least, it hasn't accepted them
recently).
In comparing rcsdiff to the new CVS implementation, I noticed that
the following rcsdiff flags are not handled by CVS diff:
-y: perform diff even when the requested revisions are the
same revision number
-q: run quietly
-T: preserve modification time on the RCS file
-z: specify timezone for use in file labels
I think these are not really relevant. -y is undocumented even in
RCS 5.7, and seems like a minor change at best. According to RCS
documentation, -T only applies when a RCS file has been modified
because of lock changes; doesn't CVS sidestep RCS's entire lock
structure? -z seems to be unsupported by CVS diff, and has a
different meaning as a global option anyway. (Adding it could be
a feature, but if it is left out for now, it should not break
anything.) For the purposes of producing output, CVS diff appears
mostly to ignore -q. Maybe this should be fixed, but I think it's
a larger issue than the changes included here. */
static void strcat_and_allocate PROTO ((char **, size_t *, const char *));
/* *STR is a pointer to a malloc'd string. *LENP is its allocated
@ -183,6 +216,8 @@ diff (argc, argv)
if (argc == -1)
usage (diff_usage);
have_rev1_label = have_rev2_label = 0;
/*
* Note that we catch all the valid arguments here, so that we can
* intercept the -r arguments for doing revision diffs; and -l/-R for a
@ -201,33 +236,44 @@ diff (argc, argv)
optind = 0;
while ((c = getopt_long (argc, argv,
"+abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:V:W:k:r:",
"+abcdefhilnpstuw0123456789BHNRC:D:F:I:L:U:V:W:k:r:",
longopts, &option_index)) != -1)
{
switch (c)
{
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'h': case 'i': case 'n': case 'p': case 's': case 't':
case 'u': case 'w': case 'y': case '0': case '1': case '2':
case 'u': case 'w': case '0': case '1': case '2':
case '3': case '4': case '5': case '6': case '7': case '8':
case '9': case 'B': case 'H': case 'T':
case '9': case 'B': case 'H':
(void) sprintf (tmp, " -%c", (char) c);
strcat_and_allocate (&opts, &opts_allocated, tmp);
break;
case 'C': case 'F': case 'I': case 'L': case 'U': case 'V':
case 'W':
case 'L':
if (have_rev1_label++)
if (have_rev2_label++)
{
error (0, 0, "extra -L arguments ignored");
break;
}
strcat_and_allocate (&opts, &opts_allocated, " -L");
strcat_and_allocate (&opts, &opts_allocated, optarg);
break;
case 'C': case 'F': case 'I': case 'U': case 'V': case 'W':
(void) sprintf (tmp, " -%c", (char) c);
strcat_and_allocate (&opts, &opts_allocated, tmp);
strcat_and_allocate (&opts, &opts_allocated, optarg);
break;
case 147:
case 131:
/* --ifdef. */
strcat_and_allocate (&opts, &opts_allocated, " -D");
strcat_and_allocate (&opts, &opts_allocated, optarg);
break;
case 129: case 130: case 131: case 132: case 133: case 134:
case 129: case 130: case 132: case 133: case 134:
case 135: case 136: case 137: case 138: case 139: case 140:
case 141: case 142: case 143: case 144: case 145: case 146:
case 147: case 148:
strcat_and_allocate (&opts, &opts_allocated, " --");
strcat_and_allocate (&opts, &opts_allocated,
longopts[option_index].name);
@ -359,6 +405,11 @@ diff_fileproc (callerdat, finfo)
char *tocvsPath;
char *fname;
/* Initialize these solely to avoid warnings from gcc -Wall about
variables that might be used uninitialized. */
tmp = NULL;
fname = NULL;
user_file_rev = 0;
vers = Version_TS (finfo, NULL, NULL, NULL, 1, 0);
@ -536,9 +587,9 @@ diff_fileproc (callerdat, finfo)
}
/* Output an "Index:" line for patch to use */
(void) fflush (stdout);
(void) printf ("Index: %s\n", finfo->fullname);
(void) fflush (stdout);
cvs_output ("Index: ", 0);
cvs_output (finfo->fullname, 0);
cvs_output ("\n", 1);
tocvsPath = wrap_tocvs_process_file(finfo->file);
if (tocvsPath)
@ -561,14 +612,20 @@ diff_fileproc (callerdat, finfo)
{
/* This is file, not fullname, because it is the "Index:" line which
is supposed to contain the directory. */
(void) printf ("===================================================================\nRCS file: %s\n",
finfo->file);
(void) printf ("diff -N %s\n", finfo->file);
cvs_output ("\
===================================================================\n\
RCS file: ", 0);
cvs_output (finfo->file, 0);
cvs_output ("\n", 1);
cvs_output ("diff -N ", 0);
cvs_output (finfo->file, 0);
cvs_output ("\n", 1);
if (empty_file == DIFF_ADDED)
{
if (use_rev2 == NULL)
run_setup ("%s %s %s %s", DIFF, opts, DEVNULL, finfo->file);
status = diff_exec (DEVNULL, finfo->file, opts, RUN_TTY);
else
{
int retcode;
@ -589,7 +646,7 @@ diff_fileproc (callerdat, finfo)
}
/* FIXME: what if retcode > 0? */
run_setup ("%s %s %s %s", DIFF, opts, DEVNULL, tmp);
status = diff_exec (DEVNULL, tmp, opts, RUN_TTY);
}
}
else
@ -610,30 +667,36 @@ diff_fileproc (callerdat, finfo)
}
/* FIXME: what if retcode > 0? */
run_setup ("%s %s %s %s", DIFF, opts, tmp, DEVNULL);
status = diff_exec (tmp, DEVNULL, opts, RUN_TTY);
}
}
else
{
if (use_rev2)
{
run_setup ("%s%s -x,v/ %s %s -r%s -r%s", Rcsbin, RCS_DIFF,
opts, *options ? options : vers->options,
use_rev1, use_rev2);
}
else
{
run_setup ("%s%s -x,v/ %s %s -r%s", Rcsbin, RCS_DIFF, opts,
*options ? options : vers->options, use_rev1);
}
run_arg (vers->srcfile->path);
char *label1 = NULL;
char *label2 = NULL;
if (!have_rev1_label)
label1 =
make_file_label (finfo->fullname, use_rev1, vers->srcfile);
if (!have_rev2_label)
label2 =
make_file_label (finfo->fullname, use_rev2, vers->srcfile);
status = RCS_exec_rcsdiff (vers->srcfile, opts,
*options ? options : vers->options,
use_rev1, use_rev2,
label1, label2,
finfo->file);
if (label1) free (label1);
if (label2) free (label2);
}
switch ((status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
RUN_REALLY|RUN_COMBINED)))
switch (status)
{
case -1: /* fork failed */
error (1, errno, "fork failed during rcsdiff of %s",
error (1, errno, "fork failed while diffing %s",
vers->srcfile->path);
case 0: /* everything ok */
err = 0;
@ -662,7 +725,6 @@ diff_fileproc (callerdat, finfo)
free (tmp);
}
(void) fflush (stdout);
freevers_ts (&vers);
diff_mark_errors (err);
return (err);
@ -696,9 +758,9 @@ diff_dirproc (callerdat, dir, pos_repos, update_dir, entries)
/* XXX - check for dirs we don't want to process??? */
/* YES ... for instance dirs that don't exist!!! -- DW */
if (!isdir (dir) )
return (R_SKIP_ALL);
if (!isdir (dir))
return (R_SKIP_ALL);
if (!quiet)
error (0, 0, "Diffing %s", update_dir);
return (R_PROCESS);
@ -855,9 +917,9 @@ diff_file_nodiff (finfo, vers, empty_file)
{
/* drop user_file_rev into first unused use_rev */
if (!use_rev1)
use_rev1 = xstrdup (user_file_rev);
use_rev1 = xstrdup (user_file_rev);
else if (!use_rev2)
use_rev2 = xstrdup (user_file_rev);
use_rev2 = xstrdup (user_file_rev);
/* and if not, it wasn't needed anyhow */
user_file_rev = 0;
}

View File

@ -3,7 +3,7 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS 1.4 kit.
* specified in the README file that comes with the CVS source distribution.
*
* "import" checks in the vendor release located in the current directory into
* the CVS source repository. The CVS vendor branch support is utilized.
@ -23,7 +23,6 @@
#define FILE_HOLDER ".#cvsxxx"
static char *get_comment PROTO((char *user));
static int expand_at_signs PROTO((char *buf, off_t size, FILE *fp));
static int add_rev PROTO((char *message, RCSNode *rcs, char *vfile,
char *vers));
static int add_tags PROTO((RCSNode *rcs, char *vfile, char *vtag, int targc,
@ -56,6 +55,7 @@ static const char *const import_usage[] =
"\t-b bra\tVendor branch id.\n",
"\t-m msg\tLog message.\n",
"\t-W spec\tWrappers specification line.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
@ -104,9 +104,9 @@ import (argc, argv)
break;
case 'm':
#ifdef FORCE_USE_EDITOR
use_editor = TRUE;
use_editor = 1;
#else
use_editor = FALSE;
use_editor = 0;
#endif
message = xstrdup(optarg);
break;
@ -199,6 +199,7 @@ import (argc, argv)
if (msglen == 0 || message[msglen - 1] != '\n')
{
char *nm = xmalloc (msglen + 2);
*nm = '\0';
if (message != NULL)
{
(void) strcpy (nm, message);
@ -467,6 +468,8 @@ process_import_file (message, vfile, vtag, targc, targv)
if (!isfile (attic_name))
{
int retval;
char *free_opt = NULL;
char *our_opt = keyword_opt;
free (attic_name);
/*
@ -474,8 +477,42 @@ process_import_file (message, vfile, vtag, targc, targv)
* repository nor in the Attic -- create it anew.
*/
add_log ('N', vfile);
retval = add_rcs_file (message, rcs, vfile, vhead, vbranch,
vtag, targc, targv, logfp);
#ifdef SERVER_SUPPORT
/* The most reliable information on whether the file is binary
is what the client told us. That is because if the client had
the wrong idea about binaryness, it corrupted the file, so
we might as well believe the client. */
if (server_active)
{
Node *node;
List *entries;
/* Reading all the entries for each file is fairly silly, and
probably slow. But I am too lazy at the moment to do
anything else. */
entries = Entries_Open (0);
node = findnode_fn (entries, vfile);
if (node != NULL)
{
Entnode *entdata = (Entnode *) node->data;
if (entdata->type == ENT_FILE)
{
assert (entdata->options[0] == '-'
&& entdata->options[1] == 'k');
our_opt = xstrdup (entdata->options + 2);
free_opt = our_opt;
}
}
Entries_Close (entries);
}
#endif
retval = add_rcs_file (message, rcs, vfile, vhead, our_opt,
vbranch, vtag, targc, targv,
NULL, 0, logfp);
if (free_opt != NULL)
free (free_opt);
free (rcs);
return retval;
}
@ -602,12 +639,14 @@ add_rev (message, rcs, vfile, vers)
/* Before RCS_lock existed, we were directing stdout, as well as
stderr, from the RCS command, to DEVNULL. I wouldn't guess that
was necessary, but I don't know for sure. */
if (RCS_lock (rcs, vbranch, 1) != 0)
{
error (0, errno, "fork failed");
return (1);
}
/* Earlier versions of this function printed a `fork failed' error
when RCS_lock returned an error code. That's not appropriate
now that RCS_lock is librarified, but should the error text be
preserved? */
if (RCS_lock (rcs, vbranch, 1) != 0)
return 1;
locked = 1;
RCS_rewrite (rcs, NULL, NULL);
}
tocvsPath = wrap_tocvs_process_file (vfile);
if (tocvsPath == NULL)
@ -632,7 +671,7 @@ add_rev (message, rcs, vfile, vers)
}
}
status = RCS_checkin (rcs->path, tocvsPath == NULL ? vfile : tocvsPath,
status = RCS_checkin (rcs, tocvsPath == NULL ? vfile : tocvsPath,
message, vbranch,
(RCS_FLAGS_QUIET
| (use_file_modtime ? RCS_FLAGS_MODTIME : 0)));
@ -656,6 +695,7 @@ add_rev (message, rcs, vfile, vers)
if (locked)
{
(void) RCS_unlock(rcs, vbranch, 0);
RCS_rewrite (rcs, NULL, NULL);
}
return (1);
}
@ -693,6 +733,7 @@ add_tags (rcs, vfile, vtag, targc, targv)
"ERROR: Failed to set tag %s in %s", vtag, rcs->path);
return (1);
}
RCS_rewrite (rcs, NULL, NULL);
memset (&finfo, 0, sizeof finfo);
finfo.file = vfile;
@ -705,7 +746,9 @@ add_tags (rcs, vfile, vtag, targc, targv)
vers = Version_TS (&finfo, NULL, vtag, NULL, 1, 0);
for (i = 0; i < targc; i++)
{
if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) != 0)
if ((retcode = RCS_settag (rcs, targv[i], vers->vn_rcs)) == 0)
RCS_rewrite (rcs, NULL, NULL);
else
{
ierrno = errno;
fperror (logfp, 0, retcode == -1 ? ierrno : 0,
@ -879,23 +922,33 @@ get_comment (user)
/* Create a new RCS file from scratch.
This probably should be moved to rcs.c now that it is called from
places outside import.c. */
places outside import.c.
Return value is 0 for success, or nonzero for failure (in which
case an error message will have already been printed). */
int
add_rcs_file (message, rcs, user, add_vhead, add_vbranch, vtag, targc, targv,
add_logfp)
/* Log message for the addition. */
add_rcs_file (message, rcs, user, add_vhead, key_opt,
add_vbranch, vtag, targc, targv,
desctext, desclen, add_logfp)
/* Log message for the addition. Not used if add_vhead == NULL. */
char *message;
/* Filename of the RCS file to create. */
char *rcs;
/* Filename of the file to serve as the contents of the initial
revision. */
revision. Even if add_vhead is NULL, we use this to determine
the modes to give the new RCS file. */
char *user;
/* Revision number of head that we are adding. Normally 1.1 but
could be another revision as long as ADD_VBRANCH is a branch
from it. */
from it. If NULL, then just add an empty file without any
revisions (similar to the one created by "rcs -i"). */
char *add_vhead;
/* Keyword expansion mode, e.g., "b" for binary. NULL means the
default behavior. */
char *key_opt;
/* Vendor branch to import to, or NULL if none. If non-NULL, then
vtag should also be non-NULL. */
char *add_vbranch;
@ -903,6 +956,11 @@ add_rcs_file (message, rcs, user, add_vhead, add_vbranch, vtag, targc, targv,
int targc;
char *targv[];
/* If non-NULL, description for the file. If NULL, the description
will be empty. */
char *desctext;
size_t desclen;
/* Write errors to here as well as via error (), or NULL if we should
use only error (). */
FILE *add_logfp;
@ -912,20 +970,24 @@ add_rcs_file (message, rcs, user, add_vhead, add_vbranch, vtag, targc, targv,
struct tm *ftm;
time_t now;
char altdate1[MAXDATELEN];
#ifndef HAVE_RCS5
char altdate2[MAXDATELEN];
#endif
char *author;
int i, ierrno, err = 0;
mode_t mode;
char *tocvsPath;
char *userfile;
char *local_opt = keyword_opt;
char *local_opt = key_opt;
char *free_opt = NULL;
if (noexec)
return (0);
/* Note that as the code stands now, the -k option overrides any
settings in wrappers (whether CVSROOT/cvswrappers, -W, or
whatever). Some have suggested this should be the other way
around. As far as I know the documentation doesn't say one way
or the other. Before making a change of this sort, should think
about what is best, document it (in cvs.texinfo and NEWS), &c. */
if (local_opt == NULL)
{
if (wrap_name_has (user, WRAP_RCSOPTION))
@ -936,7 +998,17 @@ add_rcs_file (message, rcs, user, add_vhead, add_vbranch, vtag, targc, targv,
tocvsPath = wrap_tocvs_process_file (user);
userfile = (tocvsPath == NULL ? user : tocvsPath);
fpuser = CVS_FOPEN (userfile, "r");
/* Opening in text mode is probably never the right thing for the
server (because the protocol encodes text files in a fashion
which does not depend on what the client or server OS is, as
documented in cvsclient.texi), but as long as the server just
runs on unix it is a moot point. */
fpuser = CVS_FOPEN (userfile,
((local_opt != NULL && strcmp (local_opt, "b") == 0)
? "rb"
: "r")
);
if (fpuser == NULL)
{
/* not fatal, continue import */
@ -954,8 +1026,17 @@ add_rcs_file (message, rcs, user, add_vhead, add_vbranch, vtag, targc, targv,
/*
* putadmin()
*/
if (fprintf (fprcs, "head %s;\012", add_vhead) < 0)
goto write_error;
if (add_vhead != NULL)
{
if (fprintf (fprcs, "head %s;\012", add_vhead) < 0)
goto write_error;
}
else
{
if (fprintf (fprcs, "head ;\012") < 0)
goto write_error;
}
if (add_vbranch != NULL)
{
if (fprintf (fprcs, "branch %s;\012", add_vbranch) < 0)
@ -1001,126 +1082,126 @@ add_rcs_file (message, rcs, user, add_vhead, add_vbranch, vtag, targc, targv,
if (fprintf (fprcs, "\012") < 0)
goto write_error;
/*
* puttree()
*/
/* Get information on modtime and mode. */
if (fstat (fileno (fpuser), &sb) < 0)
error (1, errno, "cannot fstat %s", user);
if (use_file_modtime)
now = sb.st_mtime;
else
(void) time (&now);
#ifdef HAVE_RCS5
ftm = gmtime (&now);
#else
ftm = localtime (&now);
#endif
(void) sprintf (altdate1, DATEFORM,
ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
ftm->tm_min, ftm->tm_sec);
#ifdef HAVE_RCS5
#define altdate2 altdate1
#else
/*
* If you don't have RCS V5 or later, you need to lie about the ci
* time, since RCS V4 and earlier insist that the times differ.
*/
now++;
ftm = localtime (&now);
(void) sprintf (altdate2, DATEFORM,
ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
ftm->tm_min, ftm->tm_sec);
#endif
author = getcaller ();
if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 ||
/* Write the revision(s), with the date and author and so on
(that is "delta" rather than "deltatext" from rcsfile(5)). */
if (add_vhead != NULL)
{
if (use_file_modtime)
now = sb.st_mtime;
else
(void) time (&now);
ftm = gmtime (&now);
(void) sprintf (altdate1, DATEFORM,
ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
ftm->tm_min, ftm->tm_sec);
author = getcaller ();
if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 ||
fprintf (fprcs, "date %s; author %s; state Exp;\012",
altdate1, author) < 0)
goto write_error;
if (fprintf (fprcs, "branches") < 0)
goto write_error;
if (add_vbranch != NULL)
{
if (fprintf (fprcs, " %s.1", add_vbranch) < 0)
if (fprintf (fprcs, "branches") < 0)
goto write_error;
}
if (fprintf (fprcs, ";\012") < 0)
goto write_error;
if (fprintf (fprcs, "next ;\012") < 0)
goto write_error;
if (add_vbranch != NULL)
{
if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
fprintf (fprcs, "date %s; author %s; state Exp;\012",
altdate2, author) < 0 ||
fprintf (fprcs, "branches ;\012") < 0 ||
fprintf (fprcs, "next ;\012\012") < 0)
goto write_error;
}
if (
/*
* putdesc()
*/
fprintf (fprcs, "\012desc\012") < 0 ||
fprintf (fprcs, "@@\012\012\012") < 0 ||
/*
* putdelta()
*/
fprintf (fprcs, "\012%s\012", add_vhead) < 0 ||
fprintf (fprcs, "log\012@") < 0)
goto write_error;
if (add_vbranch != NULL)
{
/* We are going to put the log message in the revision on the
branch. So putting it here too seems kind of redundant, I
guess (and that is what CVS has always done, anyway). */
if (fprintf (fprcs, "Initial revision\012") < 0)
goto write_error;
}
else
{
if (expand_at_signs (message, (off_t) strlen (message), fprcs) < 0)
goto write_error;
}
if (fprintf (fprcs, "@\012") < 0 ||
fprintf (fprcs, "text\012@") < 0)
{
goto write_error;
}
/* Now copy over the contents of the file, expanding at signs. */
{
char buf[8192];
unsigned int len;
while (1)
if (add_vbranch != NULL)
{
len = fread (buf, 1, sizeof buf, fpuser);
if (len == 0)
{
if (ferror (fpuser))
error (1, errno, "cannot read file %s for copying", user);
break;
}
if (expand_at_signs (buf, len, fprcs) < 0)
if (fprintf (fprcs, " %s.1", add_vbranch) < 0)
goto write_error;
}
if (fprintf (fprcs, ";\012") < 0)
goto write_error;
if (fprintf (fprcs, "next ;\012") < 0)
goto write_error;
if (add_vbranch != NULL)
{
if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
fprintf (fprcs, "date %s; author %s; state Exp;\012",
altdate1, author) < 0 ||
fprintf (fprcs, "branches ;\012") < 0 ||
fprintf (fprcs, "next ;\012\012") < 0)
goto write_error;
}
}
if (fprintf (fprcs, "@\012\012") < 0)
/* Now write the description (possibly empty). */
if (fprintf (fprcs, "\012desc\012") < 0 ||
fprintf (fprcs, "@") < 0)
goto write_error;
if (add_vbranch != NULL)
if (desctext != NULL)
{
if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
fprintf (fprcs, "log\012@") < 0 ||
expand_at_signs (message, (off_t) strlen (message), fprcs) < 0 ||
fprintf (fprcs, "@\012text\012") < 0 ||
fprintf (fprcs, "@@\012") < 0)
/* The use of off_t not size_t for the second argument is very
strange, since we are dealing with something which definitely
fits in memory. */
if (expand_at_signs (desctext, (off_t) desclen, fprcs) < 0)
goto write_error;
}
if (fprintf (fprcs, "@\012\012\012") < 0)
goto write_error;
/* Now write the log messages and contents for the revision(s) (that
is, "deltatext" rather than "delta" from rcsfile(5)). */
if (add_vhead != NULL)
{
if (fprintf (fprcs, "\012%s\012", add_vhead) < 0 ||
fprintf (fprcs, "log\012@") < 0)
goto write_error;
if (add_vbranch != NULL)
{
/* We are going to put the log message in the revision on the
branch. So putting it here too seems kind of redundant, I
guess (and that is what CVS has always done, anyway). */
if (fprintf (fprcs, "Initial revision\012") < 0)
goto write_error;
}
else
{
if (expand_at_signs (message, (off_t) strlen (message), fprcs) < 0)
goto write_error;
}
if (fprintf (fprcs, "@\012") < 0 ||
fprintf (fprcs, "text\012@") < 0)
{
goto write_error;
}
/* Now copy over the contents of the file, expanding at signs. */
{
char buf[8192];
unsigned int len;
while (1)
{
len = fread (buf, 1, sizeof buf, fpuser);
if (len == 0)
{
if (ferror (fpuser))
error (1, errno, "cannot read file %s for copying",
user);
break;
}
if (expand_at_signs (buf, len, fprcs) < 0)
goto write_error;
}
}
if (fprintf (fprcs, "@\012\012") < 0)
goto write_error;
if (add_vbranch != NULL)
{
if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
fprintf (fprcs, "log\012@") < 0 ||
expand_at_signs (message,
(off_t) strlen (message), fprcs) < 0 ||
fprintf (fprcs, "@\012text\012") < 0 ||
fprintf (fprcs, "@@\012") < 0)
goto write_error;
}
}
if (fclose (fprcs) == EOF)
{
@ -1184,7 +1265,7 @@ add_rcs_file (message, rcs, user, add_vhead, add_vbranch, vtag, targc, targv,
* signs. If an error occurs, return a negative value and set errno
* to indicate the error. If not, return a nonnegative value.
*/
static int
int
expand_at_signs (buf, size, fp)
char *buf;
off_t size;

View File

@ -3,7 +3,7 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS 1.4 kit.
* specified in the README file that comes with the CVS source distribution.
*
* Set Lock
*
@ -39,9 +39,9 @@
be.
* Readlocks ensure that we won't find the file in the state in
which it is in between the "rcs -i" and the RCS_checkin in commit.c
(when a file is being added). This state is a state in which the
RCS file parsing routines in rcs.c cannot parse the file.
which it is in between the calls to add_rcs_file and RCS_checkin in
commit.c (when a file is being added). This state is a state in
which the RCS file parsing routines in rcs.c cannot parse the file.
* Readlocks ensure that a reader won't try to look at a
half-written fileattr file (fileattr is not updated atomically).

View File

@ -3,7 +3,7 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS 1.4 kit.
* specified in the README file that comes with the CVS source distribution.
*/
#include "cvs.h"
@ -282,7 +282,7 @@ do_editor (dir, messagep, repository, changes)
(void) Parse_Info (CVSROOTADM_EDITINFO, repository, editinfo_proc, 0);
/* run the editor */
run_setup ("%s", editinfo_editor ? editinfo_editor : Editor);
run_setup (editinfo_editor ? editinfo_editor : Editor);
run_arg (fname);
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
RUN_NORMAL | RUN_SIGIGNORE)) != 0)
@ -345,8 +345,16 @@ do_editor (dir, messagep, repository, changes)
(void) printf ("Action: (continue) ");
(void) fflush (stdout);
line_length = getline (&line, &line_chars_allocated, stdin);
if (line_length <= 0
|| *line == '\n' || *line == 'c' || *line == 'C')
if (line_length < 0)
{
error (0, errno, "cannot read from stdin");
if (unlink_file (fname) < 0)
error (0, errno,
"warning: cannot remove temp file %s", fname);
error (1, 0, "aborting");
}
else if (line_length == 0
|| *line == '\n' || *line == 'c' || *line == 'C')
break;
if (*line == 'a' || *line == 'A')
{
@ -417,10 +425,7 @@ do_verify (messagep, repository)
fp = fopen (fname, "w");
if (fp == NULL)
{
error (1, errno, "cannot create temporary file %s", fname);
return;
}
else
{
fprintf (fp, "%s", *messagep);
@ -440,10 +445,15 @@ do_verify (messagep, repository)
if (verifymsg_script)
{
run_setup ("%s", verifymsg_script);
run_setup (verifymsg_script);
run_arg (fname);
if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
RUN_NORMAL | RUN_SIGIGNORE)) != 0)
{
/* Since following error() exits, delete the temp file
now. */
unlink_file (fname);
error (1, retcode == -1 ? errno : 0,
"Message verification failed");
}
@ -497,7 +507,7 @@ do_verify (messagep, repository)
if (fclose (fp) < 0)
error (0, errno, "warning: cannot close %s", fname);
/* Close and delete the temp file */
/* Delete the temp file */
unlink_file (fname);
free (fname);

View File

@ -3,7 +3,7 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License
* as specified in the README file that comes with the CVS 1.4 kit.
* as specified in the README file that comes with the CVS source distribution.
*
* This is the main C driver for the CVS system.
*
@ -33,15 +33,15 @@ char *command_name;
char hostname[MAXHOSTNAMELEN];
int use_editor = TRUE;
int use_cvsrc = TRUE;
int use_editor = 1;
int use_cvsrc = 1;
int cvswrite = !CVSREAD_DFLT;
int really_quiet = FALSE;
int quiet = FALSE;
int trace = FALSE;
int noexec = FALSE;
int readonlyfs = FALSE;
int logoff = FALSE;
int really_quiet = 0;
int quiet = 0;
int trace = 0;
int noexec = 0;
int readonlyfs = 0;
int logoff = 0;
mode_t cvsumask = UMASK_DFLT;
char *CurDir;
@ -49,7 +49,6 @@ char *CurDir;
/*
* Defaults, for the environment variables that are not set
*/
char *Rcsbin = RCSBIN_DFLT;
char *Tmpdir = TMPDIR_DFLT;
char *Editor = EDITOR_DFLT;
@ -125,33 +124,47 @@ static const struct cmd
static const char *const usg[] =
{
"Usage: %s [cvs-options] command [command-options] [files...]\n",
" Where 'cvs-options' are:\n",
" -H Displays Usage information for command\n",
" -Q Cause CVS to be really quiet.\n",
" -q Cause CVS to be somewhat quiet.\n",
" -r Make checked-out files read-only\n",
" -w Make checked-out files read-write (default)\n",
" -l Turn History logging off\n",
" -n Do not execute anything that will change the disk\n",
" -R Assume repository is read-only, such as CDROM\n",
" -t Show trace of program execution -- Try with -n\n",
" -v CVS version and copyright\n",
" -b bindir Find RCS programs in 'bindir'\n",
" -T tmpdir Use 'tmpdir' for temporary files\n",
" -e editor Use 'editor' for editing log information\n",
" -d CVS_root Overrides $CVSROOT as the root of the CVS tree\n",
" -f Do not use the ~/.cvsrc file\n",
#ifdef CLIENT_SUPPORT
" -z # Use compression level '#' for net traffic.\n",
#ifdef ENCRYPTION
" -x Encrypt all net traffic.\n",
#endif
#endif
" -s VAR=VAL Set CVS user variable.\n",
/* CVS usage messages never have followed the GNU convention of
putting metavariables in uppercase. I don't know whether that
is a good convention or not, but if it changes it would have to
change in all the usage messages. For now, they consistently
use lowercase, as far as I know. Puncutation is pretty funky,
though. Sometimes they use none, as here. Sometimes they use
single quotes (not the TeX-ish `' stuff), as in --help-options.
Sometimes they use double quotes, as in cvs -H add.
Most (not all) of the usage messages seem to have periods at
the end of each line. I haven't tried to duplicate this style
in --help as it is a rather different format from the rest. */
"Usage: %s [cvs-options] command [command-options-and-arguments]\n",
" where cvs-options are -q, -n, etc.\n",
" (specify --help-options for a list of options)\n",
" where command is add, admin, etc.\n",
" (specify --help-commands for a list of commands\n",
" or --help-synonyms for a list of command synonyms)\n",
" where command-options-and-arguments depend on the specific command\n",
" (specify -H followed by a command name for command-specific help)\n",
" Specify --help to receive this message\n",
"\n",
" and where 'command' is: add, admin, etc. (use the --help-commands\n",
" option for a list of commands)\n",
/* Some people think that a bug-reporting address should go here. IMHO,
the web sites are better because anything else is very likely to go
obsolete in the years between a release and when someone might be
reading this help. Besides, we could never adequately discuss
bug reporting in a concise enough way to put in a help message. */
/* I was going to put this at the top, but usage() wants the %s to
be in the first line. */
"The Concurrent Versions System (CVS) is a tool for version control.\n",
/* I really don't think I want to try to define "version control"
in one line. I'm not sure one can get more concise than the
paragraph in ../cvs.spec without assuming the reader knows what
version control means. */
"For CVS updates and additional information, see\n",
" Cyclic Software at http://www.cyclic.com/ or\n",
" Pascal Molli's CVS site at http://www.loria.fr/~molli/cvs-index.html\n",
NULL,
};
@ -185,17 +198,48 @@ static const char *const cmd_usage[] =
" update Bring work tree in sync with repository\n",
" watch Set watches\n",
" watchers See who is watching a file\n",
"(Use the --help-synonyms option for a list of alternate command names)\n",
"(Specify the --help option for a list of other help options)\n",
NULL,
};
static const char *const opt_usage[] =
{
"CVS global options (specified before the command name) are:\n",
" -H Displays usage information for command.\n",
" -Q Cause CVS to be really quiet.\n",
" -q Cause CVS to be somewhat quiet.\n",
" -r Make checked-out files read-only.\n",
" -w Make checked-out files read-write (default).\n",
" -l Turn history logging off.\n",
" -n Do not execute anything that will change the disk.\n",
" -t Show trace of program execution -- try with -n.\n",
" -R Assume repository is read-only, such as CDROM\n",
" -v CVS version and copyright.\n",
" -b bindir Find RCS programs in 'bindir'.\n",
" -T tmpdir Use 'tmpdir' for temporary files.\n",
" -e editor Use 'editor' for editing log information.\n",
" -d CVS_root Overrides $CVSROOT as the root of the CVS tree.\n",
" -f Do not use the ~/.cvsrc file.\n",
#ifdef CLIENT_SUPPORT
" -z # Use compression level '#' for net traffic.\n",
#ifdef ENCRYPTION
" -x Encrypt all net traffic.\n",
#endif
" -a Authenticate all net traffic.\n",
#endif
" -s VAR=VAL Set CVS user variable.\n",
"(Specify the --help option for a list of other help options)\n",
NULL
};
static const char * const*
cmd_synonyms ()
{
char ** synonyms;
char ** line;
const struct cmd *c = &cmds[0];
int numcmds = 2; /* two more for title and end */
/* Three more for title, "specify --help" line, and NULL. */
int numcmds = 3;
while (c->fullname != NULL)
{
@ -220,6 +264,7 @@ cmd_synonyms ()
line++;
}
}
*line++ = "(Specify the --help option for a list of other help options)\n";
*line = NULL;
return (const char * const*) synonyms; /* will never be freed */
@ -330,11 +375,10 @@ main (argc, argv)
char *cp, *end;
const struct cmd *cm;
int c, err = 0;
int rcsbin_update_env, tmpdir_update_env, cvs_update_env;
int tmpdir_update_env, cvs_update_env;
int free_CVSroot = 0;
int free_Editor = 0;
int free_Tmpdir = 0;
int free_Rcsbin = 0;
int help = 0; /* Has the user asked for help? This
lets us support the `cvs -H cmd'
@ -345,6 +389,7 @@ main (argc, argv)
{"version", 0, NULL, 'v'},
{"help-commands", 0, NULL, 1},
{"help-synonyms", 0, NULL, 2},
{"help-options", 0, NULL, 4},
{"allow-root", required_argument, NULL, 3},
{0, 0, 0, 0}
};
@ -382,12 +427,6 @@ main (argc, argv)
* they can be overridden by command line arguments
*/
cvs_update_env = 0;
rcsbin_update_env = *Rcsbin; /* RCSBIN_DFLT must be set */
if ((cp = getenv (RCSBIN_ENV)) != NULL)
{
Rcsbin = cp;
rcsbin_update_env = 0; /* it's already there */
}
tmpdir_update_env = *Tmpdir; /* TMPDIR_DFLT must be set */
if ((cp = getenv (TMPDIR_ENV)) != NULL)
{
@ -406,10 +445,10 @@ main (argc, argv)
cvs_update_env = 0; /* it's already there */
}
if (getenv (CVSREAD_ENV) != NULL)
cvswrite = FALSE;
cvswrite = 0;
if (getenv (CVSREADONLYFS_ENV) != NULL) {
readonlyfs = TRUE;
logoff = TRUE;
readonlyfs = 1;
logoff = 1;
}
/* Set this to 0 to force getopt initialization. getopt() sets
@ -427,7 +466,7 @@ main (argc, argv)
!= EOF)
{
if (c == 'f')
use_cvsrc = FALSE;
use_cvsrc = 0;
}
/*
@ -440,7 +479,7 @@ main (argc, argv)
opterr = 1;
while ((c = getopt_long
(argc, argv, "+QqrwtnRlvb:T:e:d:Hfz:s:x", long_options, &option_index))
(argc, argv, "+QqrwtnRlvb:T:e:d:Hfz:s:xa", long_options, &option_index))
!= EOF)
{
switch (c)
@ -453,51 +492,63 @@ main (argc, argv)
/* --help-synonyms */
usage (cmd_synonyms());
break;
case 4:
/* --help-options */
usage (opt_usage);
break;
case 3:
/* --allow-root */
root_allow_add (optarg);
break;
case 'Q':
really_quiet = TRUE;
really_quiet = 1;
/* FALL THROUGH */
case 'q':
quiet = TRUE;
quiet = 1;
break;
case 'r':
cvswrite = FALSE;
cvswrite = 0;
break;
case 'w':
cvswrite = TRUE;
cvswrite = 1;
break;
case 't':
trace = TRUE;
trace = 1;
break;
case 'R':
readonlyfs = TRUE;
logoff = TRUE;
readonlyfs = 1;
logoff = 1;
break;
case 'n':
noexec = TRUE;
noexec = 1;
case 'l': /* Fall through */
logoff = TRUE;
logoff = 1;
break;
case 'v':
/* Having the year here is a good idea, so people have
some idea of how long ago their version of CVS was
released. */
(void) fputs (version_string, stdout);
(void) fputs (config_string, 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 ("Copyright (c) 1992, Brian Berliner and Jeff Polk\n", stdout);
(void) fputs ("Copyright (c) 1989-1992, Brian Berliner\n", stdout);
(void) fputs ("\
Copyright (c) 1989-1997 Brian Berliner, david d `zoo' zuhn, \n\
Jeff Polk, and other authors\n", stdout);
(void) fputs ("\n", stdout);
(void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
(void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
(void) fputs ("\n", stdout);
(void) fputs ("Specify the --help option for further information about CVS\n", stdout);
exit (0);
break;
case 'b':
Rcsbin = xstrdup (optarg);
free_Rcsbin = 1;
rcsbin_update_env = 1; /* need to update environment */
/* This option used to specify the directory for RCS
executables. But since we don't run them any more,
this is a noop. Silently ignore it so that .cvsrc
and scripts and inetd.conf and such can work with
either new or old CVS. */
break;
case 'T':
Tmpdir = xstrdup (optarg);
@ -517,7 +568,7 @@ main (argc, argv)
help = 1;
break;
case 'f':
use_cvsrc = FALSE; /* unnecessary, since we've done it above */
use_cvsrc = 0; /* unnecessary, since we've done it above */
break;
case 'z':
#ifdef CLIENT_SUPPORT
@ -542,6 +593,15 @@ main (argc, argv)
If no ENCRYPTION, we still accept -x, but issue an
error if we are being run as a client. */
break;
case 'a':
#ifdef CLIENT_SUPPORT
cvsauthenticate = 1;
#endif
/* If no CLIENT_SUPPORT, ignore -a, so that users can
have it in their .cvsrc and not cause any trouble.
We will issue an error later if stream
authentication is not supported. */
break;
case '?':
default:
usage (usg);
@ -572,6 +632,9 @@ main (argc, argv)
else
command_name = cm->fullname; /* Global pointer for later use */
/* This should probably remain a warning, rather than an error,
for quite a while. For one thing the version of VC distributed
with GNU emacs 19.34 invokes 'cvs rlog' instead of 'cvs log'. */
if (strcmp (argv[0], "rlog") == 0)
{
error (0, 0, "warning: the rlog command is deprecated");
@ -615,7 +678,7 @@ main (argc, argv)
#endif /* HAVE_KERBEROS */
#if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT)
#if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
if (strcmp (command_name, "pserver") == 0)
{
/* The reason that --allow-root is not a command option
@ -632,15 +695,16 @@ main (argc, argv)
/* Pretend we were invoked as a plain server. */
command_name = "server";
}
#endif /* AUTH_SERVER_SUPPORT && SERVER_SUPPORT */
#endif /* (AUTH_SERVER_SUPPORT || HAVE_GSSAPI) && SERVER_SUPPORT */
#ifdef SERVER_SUPPORT
server_active = strcmp (command_name, "server") == 0;
/* Fiddling with CVSROOT doesn't make sense if we're running
in server mode, since the client will send the repository
directory after the connection is made. */
#ifdef SERVER_SUPPORT
if (strcmp (command_name, "server") != 0)
if (!server_active)
#endif
{
char *CVSADM_Root;
@ -788,7 +852,7 @@ main (argc, argv)
it is worth the trouble. */
#ifdef SERVER_SUPPORT
if (strcmp (command_name, "server") == 0)
if (server_active)
CurDir = xstrdup ("<remote>");
else
#endif
@ -802,16 +866,6 @@ main (argc, argv)
Tmpdir = "/tmp";
#ifdef HAVE_PUTENV
/* Now, see if we should update the environment with the
Rcsbin value */
if (rcsbin_update_env)
{
char *env;
env = xmalloc (strlen (RCSBIN_ENV) + strlen (Rcsbin) + 1 + 1);
(void) sprintf (env, "%s=%s", RCSBIN_ENV, Rcsbin);
(void) putenv (env);
/* do not free env, as putenv has control of it */
}
if (tmpdir_update_env)
{
char *env;
@ -828,24 +882,6 @@ main (argc, argv)
}
#endif
/*
* If Rcsbin is set to something, make sure it is terminated with
* a slash character. If not, add one.
*/
if (*Rcsbin)
{
int len = strlen (Rcsbin);
char *rcsbin;
if (Rcsbin[len - 1] != '/')
{
rcsbin = Rcsbin;
Rcsbin = xmalloc (len + 2); /* one for '/', one for NULL */
(void) strcpy (Rcsbin, rcsbin);
(void) strcat (Rcsbin, "/");
}
}
#ifndef DONT_USE_SIGNALS
/* make sure we clean up on error */
#ifdef SIGHUP
@ -883,6 +919,28 @@ main (argc, argv)
if (use_cvsrc)
read_cvsrc (&argc, &argv, command_name);
/* Parse the CVSROOT/config file, but only for local. For the
server, we parse it after we know $CVSROOT. For the
client, it doesn't get parsed at all, obviously. The
presence of the parse_config call here is not mean to
predetermine whether CVSROOT/config overrides things from
read_cvsrc and other such places or vice versa. That sort
of thing probably needs more thought. */
if (1
#ifdef SERVER_SUPPORT
&& !server_active
#endif
#ifdef CLIENT_SUPPORT
&& !client_active
#endif
)
{
/* If there was an error parsing the config file, parse_config
already printed an error. We keep going. Why? Because
if we didn't, then there would be no way to check in a new
CVSROOT/config file to fix the broken one! */
parse_config (CVSroot_directory);
}
} /* end of stuff that gets done if the user DOESN'T ask for help */
err = (*(cm->func)) (argc, argv);
@ -891,7 +949,10 @@ main (argc, argv)
{
/* Update the CVS/Root file. We might want to do this in
all directories that we recurse into, but currently we
don't. */
don't. Note that if there is an error writing the file,
we give an error/warning. This is so if users try to rewrite
CVS/Root with the -d option (a documented feature), they will
either succeed, or be told why it didn't work. */
Create_Root (NULL, CVSroot);
}
@ -904,8 +965,6 @@ main (argc, argv)
free (Editor);
if (free_Tmpdir)
free (Tmpdir);
if (free_Rcsbin)
free (Rcsbin);
root_allow_free ();
#ifdef SYSTEM_CLEANUP
@ -917,6 +976,8 @@ main (argc, argv)
/* This is exit rather than return because apparently that keeps
some tools which check for memory leaks happier. */
exit (err ? EXIT_FAILURE : 0);
/* Keep picky/stupid compilers (e.g. Visual C++ 5.0) happy. */
return 0;
}
char *
@ -931,11 +992,14 @@ Make_Date (rawdate)
unixtime = get_date (rawdate, (struct timeb *) NULL);
if (unixtime == (time_t) - 1)
error (1, 0, "Can't parse date/time: %s", rawdate);
#ifdef HAVE_RCS5
ftm = gmtime (&unixtime);
#else
ftm = localtime (&unixtime);
#endif
if (ftm == NULL)
/* This is a system, like VMS, where the system clock is in local
time. Hopefully using localtime here matches the "zero timezone"
hack I added to get_date. */
ftm = localtime (&unixtime);
(void) sprintf (date, DATEFORM,
ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,

View File

@ -275,6 +275,12 @@ static const char *const modules_contents[] = {
NULL
};
static const char *const config_contents[] = {
"# Set this to \"no\" if pserver shouldn't check system users/passwords\n",
"#SystemAuth=no\n",
NULL
};
static const struct admin_file filelist[] = {
{CVSROOTADM_LOGINFO,
"no logging of 'cvs commit' messages is done without a %s file",
@ -316,14 +322,27 @@ static const struct admin_file filelist[] = {
{CVSROOTADM_WRITERS,
"a %s file specifies read/write users",
NULL},
/* Some have suggested listing CVSROOTADM_PASSWD here too. The
security implications of transmitting hashed passwords over the
net are no worse than transmitting cleartext passwords which pserver
does, so this isn't a problem. But I'm worried about the implications
of storing old passwords--if someone used a password in the past
they might be using it elsewhere, using a similar password, etc,
and so it doesn't seem to me like we should be saving old passwords,
even hashed. */
/* Some have suggested listing CVSROOTADM_PASSWD here too. This
would mean that CVS commands which operate on the
CVSROOTADM_PASSWD file would transmit hashed passwords over the
net. This might seem to be no big deal, as pserver normally
transmits cleartext passwords, but the difference is that
CVSROOTADM_PASSWD contains *all* passwords, not just the ones
currently being used. For example, it could be too easy to
accidentally give someone readonly access to CVSROOTADM_PASSWD
(e.g. via anonymous CVS or cvsweb), and then if there are any
guessable passwords for read/write access (usually there will be)
they get read/write access.
Another worry is the implications of storing old passwords--if
someone used a password in the past they might be using it
elsewhere, using a similar password, etc, and so saving old
passwords, even hashed, is probably not a good idea. */
{CVSROOTADM_CONFIG,
"a %s file configures various behaviors",
config_contents},
{NULL, NULL}
};
@ -694,6 +713,7 @@ rename_rcsfile (temp, real)
const char *const init_usage[] = {
"Usage: %s %s\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
@ -785,8 +805,12 @@ init (argc, argv)
"initial checkin" but I fail to see the point as we know what
file it is from the name. */
retcode = add_rcs_file ("initial checkin", info_v,
fileptr->filename, "1.1", NULL, NULL,
0, NULL, NULL);
fileptr->filename, "1.1", NULL,
/* No vendor branch. */
NULL, NULL, 0, NULL,
NULL, 0, NULL);
if (retcode != 0)
/* add_rcs_file already printed an error message. */
err = 1;

View File

@ -2,7 +2,7 @@
* Copyright (c) 1992, Brian Berliner and Jeff Polk
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS 1.4 kit.
* specified in the README file that comes with the CVS source distribution.
*
* General recursion handler
*
@ -157,7 +157,25 @@ start_recursion (fileproc, filesdoneproc, direntproc, dirleaveproc, callerdat,
* called with the list of sub-dirs of the current dir as args
*/
if ((which & W_LOCAL) && !isdir (CVSADM))
{
dirlist = Find_Directories ((char *) NULL, W_LOCAL, (List *) NULL);
/* If there are no sub-directories, there is a certain logic in
favor of doing nothing, but in fact probably the user is just
confused about what directory they are in, or whether they
cvs add'd a new directory. In the case of at least one
sub-directory, at least when we recurse into them we
notice (hopefully) whether they are under CVS control. */
if (list_isempty (dirlist))
{
if (update_dir[0] == '\0')
error (0, 0, "in directory .:");
else
error (0, 0, "in directory %s:", update_dir);
error (1, 0,
"there is no version here; run '%s checkout' first",
program_name);
}
}
else
addlist (&dirlist, ".");
@ -637,7 +655,7 @@ but CVS uses %s for its own purposes; skipping %s directory",
else
{
newrepos = xmalloc (strlen (repository) + strlen (dir) + 5);
(void) sprintf (newrepos, "%s/%s", repository, dir);
sprintf (newrepos, "%s/%s", repository, dir);
}
}
else
@ -651,10 +669,78 @@ but CVS uses %s for its own purposes; skipping %s directory",
newrepos = xstrdup (repository);
}
/* Check to see that the CVSADM directory, if it exists, seems to be
well-formed. It can be missing files if the user hit ^C in the
middle of a previous run. We want to (a) make this a nonfatal
error, and (b) make sure we print which directory has the
problem.
Do this before the direntproc, so that (1) the direntproc
doesn't have to guess/deduce whether we will skip the directory
(e.g. send_dirent_proc and whether to send the directory), and
(2) so that the warm fuzzy doesn't get printed if we skip the
directory. */
if (frame->which & W_LOCAL)
{
char *cvsadmdir;
cvsadmdir = xmalloc (strlen (dir)
+ sizeof (CVSADM_REP)
+ sizeof (CVSADM_ENT)
+ 80);
strcpy (cvsadmdir, dir);
strcat (cvsadmdir, "/");
strcat (cvsadmdir, CVSADM);
if (isdir (cvsadmdir))
{
strcpy (cvsadmdir, dir);
strcat (cvsadmdir, "/");
strcat (cvsadmdir, CVSADM_REP);
if (!isfile (cvsadmdir))
{
/* Some commands like update may have printed "? foo" but
if we were planning to recurse, and don't on account of
CVS/Repository, we want to say why. */
error (0, 0, "ignoring %s (%s missing)", update_dir,
CVSADM_REP);
dir_return = R_SKIP_ALL;
}
/* Likewise for CVS/Entries. */
if (dir_return != R_SKIP_ALL)
{
strcpy (cvsadmdir, dir);
strcat (cvsadmdir, "/");
strcat (cvsadmdir, CVSADM_ENT);
if (!isfile (cvsadmdir))
{
/* Some commands like update may have printed "? foo" but
if we were planning to recurse, and don't on account of
CVS/Repository, we want to say why. */
error (0, 0, "ignoring %s (%s missing)", update_dir,
CVSADM_ENT);
dir_return = R_SKIP_ALL;
}
}
}
free (cvsadmdir);
}
/* call-back dir entry proc (if any) */
if (frame->direntproc != NULL)
if (dir_return == R_SKIP_ALL)
;
else if (frame->direntproc != NULL)
dir_return = frame->direntproc (frame->callerdat, dir, newrepos,
update_dir, frent->entries);
else
{
/* Generic behavior. I don't see a reason to make the caller specify
a direntproc just to get this. */
if ((frame->which & W_LOCAL) && !isdir (dir))
dir_return = R_SKIP_ALL;
}
free (newrepos);
/* only process the dir if the return code was 0 */

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
* Copyright (c) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS 1.4 kit.
* specified in the README file that comes with the CVS source distribution.
*
* "update" updates the version in the present directory with respect to the RCS
* repository. The present version must have been created by "checkout". The
@ -64,12 +64,8 @@ static int update_fileproc PROTO ((void *callerdat, struct file_info *));
static int update_filesdone_proc PROTO ((void *callerdat, int err,
char *repository, char *update_dir,
List *entries));
static int write_letter PROTO((char *file, int letter, char *update_dir));
#ifdef SERVER_SUPPORT
static void write_letter PROTO ((struct file_info *finfo, int letter));
static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts));
#else
static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts));
#endif
static char *options = NULL;
static char *tag = NULL;
@ -113,6 +109,7 @@ static const char *const update_usage[] =
"\t-j rev\tMerge in changes made between current revision and rev.\n",
"\t-I ign\tMore files to ignore (! to reset).\n",
"\t-W spec\tWrappers specification line.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
@ -259,16 +256,18 @@ update (argc, argv)
/* If the server supports the command "update-patches", that means
that it knows how to handle the -u argument to update, which
means to send patches instead of complete files. */
means to send patches instead of complete files.
We don't send -u if failed_patches != NULL, so that the
server doesn't try to send patches which will just fail
again. At least currently, the client also clobbers the
file and tells the server it is lost, which also will get
a full file instead of a patch, but it seems clean to omit
-u. */
if (failed_patches == NULL)
{
#ifndef DONT_USE_PATCH
/* Systems which don't have the patch program ported to them
will want to define DONT_USE_PATCH; then CVS won't try to
invoke patch. */
if (supported_request ("update-patches"))
send_arg ("-u");
#endif
}
if (failed_patches == NULL)
@ -548,44 +547,17 @@ update_fileproc (callerdat, finfo)
break;
case T_CONFLICT: /* old punt-type errors */
retval = 1;
(void) write_letter (finfo->file, 'C', finfo->update_dir);
write_letter (finfo, 'C');
break;
case T_NEEDS_MERGE: /* needs merging */
if (noexec)
{
retval = 1;
(void) write_letter (finfo->file, 'C', finfo->update_dir);
write_letter (finfo, 'C');
}
else
{
if (wrap_merge_is_copy (finfo->file))
#if 0
/* Look, we can't clobber the user's file. We
know it is modified and we're going to
overwrite their mod? Puh-leeze. The
correct behavior is probably something like
what merge_file does for -kb, which is to
give the users both files and tell them
what the two filenames are. Of course, -m
in wrappers needs to be documented *much*
better. Anyway, until then, make this a
fatal error. */
/* Should we be warning the user that we are
* overwriting the user's copy of the file? */
retval =
checkout_file (finfo, vers, 0);
#else
{
error (0, 0, "A -m 'COPY' wrapper is specified");
error (0, 0, "but file %s needs merge",
finfo->fullname);
error (1, 0, "\
You probably want to avoid -m 'COPY' wrappers");
#endif
}
else
retval = merge_file (finfo, vers);
retval = merge_file (finfo, vers);
}
break;
case T_MODIFIED: /* locally modified */
@ -622,7 +594,7 @@ You probably want to avoid -m 'COPY' wrappers");
if (!retcode)
{
(void) write_letter (finfo->file, 'C', finfo->update_dir);
write_letter (finfo, 'C');
retval = 1;
}
else
@ -634,7 +606,10 @@ You probably want to avoid -m 'COPY' wrappers");
}
}
if (!retval)
retval = write_letter (finfo->file, 'M', finfo->update_dir);
{
write_letter (finfo, 'M');
retval = 0;
}
break;
#ifdef SERVER_SUPPORT
case T_PATCH: /* needs patch */
@ -658,10 +633,9 @@ You probably want to avoid -m 'COPY' wrappers");
break;
}
}
/* Fall through. */
/* If we're not running as a server, just check the
file out. It's simpler and faster than starting up
two new processes (diff and patch). */
file out. It's simpler and faster than producing
and applying patches. */
/* Fall through. */
#endif
case T_CHECKOUT: /* needs checkout */
@ -674,10 +648,12 @@ You probably want to avoid -m 'COPY' wrappers");
#endif
break;
case T_ADDED: /* added but not committed */
retval = write_letter (finfo->file, 'A', finfo->update_dir);
write_letter (finfo, 'A');
retval = 0;
break;
case T_REMOVED: /* removed but not committed */
retval = write_letter (finfo->file, 'R', finfo->update_dir);
write_letter (finfo, 'R');
retval = 0;
break;
case T_REMOVE_ENTRY: /* needs to be un-registered */
retval = scratch_file (finfo);
@ -727,7 +703,23 @@ update_ignproc (file, dir)
char *file;
char *dir;
{
(void) write_letter (file, '?', dir);
struct file_info finfo;
memset (&finfo, 0, sizeof (finfo));
finfo.file = file;
finfo.update_dir = dir;
if (dir[0] == '\0')
finfo.fullname = xstrdup (file);
else
{
finfo.fullname = xmalloc (strlen (file) + strlen (dir) + 10);
strcpy (finfo.fullname, dir);
strcat (finfo.fullname, "/");
strcat (finfo.fullname, file);
}
write_letter (&finfo, '?');
free (finfo.fullname);
}
/* ARGSUSED */
@ -808,7 +800,7 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries)
if (noexec)
{
/* ignore the missing dir if -n is specified */
error (0, 0, "New directory `%s' -- ignored", dir);
error (0, 0, "New directory `%s' -- ignored", update_dir);
return (R_SKIP_ALL);
}
else
@ -818,6 +810,7 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries)
Create_Admin (dir, update_dir, repository, tag, date,
/* This is a guess. We will rewrite it later
via WriteTag. */
0,
0);
rewrite_tag = 1;
nonbranch = 0;
@ -920,7 +913,8 @@ update_dirleave_proc (callerdat, dir, err, update_dir, entries)
{
if ((cp = strrchr (line, '\n')) != NULL)
*cp = '\0';
run_setup ("%s %s", line, repository);
run_setup (line);
run_arg (repository);
cvs_output (program_name, 0);
cvs_output (" ", 1);
cvs_output (command_name, 0);
@ -1080,6 +1074,9 @@ checkout_file (finfo, vers_ts, adding)
int status;
int file_is_dead;
/* Solely to suppress a warning from gcc -Wall. */
backup = NULL;
/* don't screw with backup files if we're going to stdout */
if (!pipeout)
{
@ -1093,7 +1090,13 @@ checkout_file (finfo, vers_ts, adding)
else
/* If -f/-t wrappers are being used to wrap up a directory,
then backup might be a directory instead of just a file. */
(void) unlink_file_dir (backup);
if (unlink_file_dir (backup) < 0)
{
/* Not sure if the existence_error check is needed here. */
if (!existence_error (errno))
/* FIXME: should include update_dir in message. */
error (0, errno, "error removing %s", backup);
}
}
file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs);
@ -1133,7 +1136,7 @@ VERS: ", 0);
{
Vers_TS *xvers_ts;
if (cvswrite == TRUE
if (cvswrite
&& !file_is_dead
&& !fileattr_get (finfo->file, "_watched"))
xchmod (finfo->file, 1);
@ -1155,13 +1158,11 @@ VERS: ", 0);
}
/* set the time from the RCS file iff it was unknown before */
if (vers_ts->vn_user == NULL ||
strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
{
set_time = 1;
}
else
set_time = 0;
set_time =
(!noexec
&& (vers_ts->vn_user == NULL ||
strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
&& !file_is_dead);
wrap_fromcvs_process_file (finfo->file);
@ -1222,7 +1223,7 @@ VERS: ", 0);
if (!really_quiet && !file_is_dead)
{
write_letter (finfo->file, 'U', finfo->update_dir);
write_letter (finfo, 'U');
}
}
}
@ -1243,7 +1244,13 @@ VERS: ", 0);
{
/* If -f/-t wrappers are being used to wrap up a directory,
then backup might be a directory instead of just a file. */
(void) unlink_file_dir (backup);
if (unlink_file_dir (backup) < 0)
{
/* Not sure if the existence_error check is needed here. */
if (!existence_error (errno))
/* FIXME: should include update_dir in message. */
error (0, errno, "error removing %s", backup);
}
free (backup);
}
@ -1306,6 +1313,27 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
return 0;
}
/* First check that the first revision exists. If it has been nuked
by cvs admin -o, then just fall back to checking out entire
revisions. In some sense maybe we don't have to do this; after
all cvs.texinfo says "Make sure that no-one has checked out a
copy of the revision you outdate" but then again, that advice
doesn't really make complete sense, because "cvs admin" operates
on a working directory and so _someone_ will almost always have
_some_ revision checked out. */
{
char *rev;
rev = RCS_gettag (finfo->rcs, vers_ts->vn_user, 1, NULL);
if (rev == NULL)
{
*docheckout = 1;
return 0;
}
else
free (rev);
}
backup = xmalloc (strlen (finfo->file)
+ sizeof (CVSADM)
+ sizeof (CVSPREFIX)
@ -1382,7 +1410,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
retcode = 0;
if (! fail)
{
const char *diff_options;
char *diff_options;
/* FIXME: It might be better to come up with a diff library
which can be shared with the diffutils. */
@ -1400,21 +1428,22 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
}
else
{
/* FIXME: We should use -a if diff supports it. We should
probably just copy over most or all of the diff
handling in the RCS configure script. */
/* IMHO, we shouldn't copy over anything which even
vaguely resembles the RCS configure script. That kind of
thing tends to be ugly, slow, and fragile. It also is a
a support headache for CVS to behave differently in subtle
ways based on whether it was installed correctly. Instead we
should come up with a diff library. -kingdon, Apr 1997. */
/* Now that diff is librarified, we could be passing -a if
we wanted to. However, it is unclear to me whether we
would want to. Does diff -a, in any significant
percentage of cases, produce patches which are smaller
than the files it is patching? I guess maybe text
files with character sets which diff regards as
'binary'. Conversely, do they tend to be much larger
in the bad cases? This needs some more
thought/investigation, I suspect. */
diff_options = "-n";
}
run_setup ("%s %s %s %s", DIFF, diff_options, file1, file2);
retcode = diff_exec (file1, file2, diff_options, finfo->file);
/* A retcode of 0 means no differences. 1 means some differences. */
if ((retcode = run_exec (RUN_TTY, finfo->file, RUN_TTY, RUN_NORMAL)) != 0
if (retcode != 0
&& retcode != 1)
{
fail = 1;
@ -1434,7 +1463,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
file_info->st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH))
< 0)
error (0, errno, "cannot change mode of file %s", finfo->file);
if (cvswrite == TRUE
if (cvswrite
&& !fileattr_get (finfo->file, "_watched"))
xchmod (finfo->file, 1);
@ -1482,7 +1511,7 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
if (!really_quiet)
{
write_letter (finfo->file, 'P', finfo->update_dir);
write_letter (finfo, 'P');
}
}
else
@ -1528,7 +1557,7 @@ patch_file_write (callerdat, buffer, len)
data->final_nl = (buffer[len - 1] == '\n');
if (data->compute_checksum)
MD5Update (&data->context, buffer, len);
MD5Update (&data->context, (unsigned char *) buffer, len);
}
#endif /* SERVER_SUPPORT */
@ -1537,27 +1566,45 @@ patch_file_write (callerdat, buffer, len)
* Several of the types we process only print a bit of information consisting
* of a single letter and the name.
*/
static int
write_letter (file, letter, update_dir)
char *file;
static void
write_letter (finfo, letter)
struct file_info *finfo;
int letter;
char *update_dir;
{
if (!really_quiet)
{
char buf[2];
char *tag = NULL;
/* Big enough for "+updated" or any of its ilk. */
char buf[80];
switch (letter)
{
case 'U':
tag = "updated";
break;
default:
/* We don't yet support tagged output except for "U". */
break;
}
if (tag != NULL)
{
sprintf (buf, "+%s", tag);
cvs_output_tagged (buf, NULL);
}
buf[0] = letter;
buf[1] = ' ';
cvs_output (buf, 2);
if (update_dir[0])
buf[2] = '\0';
cvs_output_tagged ("text", buf);
cvs_output_tagged ("fname", finfo->fullname);
cvs_output_tagged ("newline", NULL);
if (tag != NULL)
{
cvs_output (update_dir, 0);
cvs_output ("/", 1);
sprintf (buf, "-%s", tag);
cvs_output_tagged (buf, NULL);
}
cvs_output (file, 0);
cvs_output ("\n", 1);
}
return (0);
return;
}
/*
@ -1590,7 +1637,8 @@ merge_file (finfo, vers)
copy_file (finfo->file, backup);
xchmod (finfo->file, 1);
if (strcmp (vers->options, "-kb") == 0)
if (strcmp (vers->options, "-kb") == 0
|| wrap_merge_is_copy (finfo->file))
{
/* For binary files, a merge is always a conflict. We give the
user the two files, and let them resolve it. It is possible
@ -1609,11 +1657,15 @@ merge_file (finfo, vers)
(struct stat *) NULL, (unsigned char *) NULL);
}
#endif
error (0, 0, "binary file needs merge");
/* Is there a better term than "nonmergeable file"? What we
really mean is, not something that CVS cannot or does not
want to merge (there might be an external manual or
automatic merge process). */
error (0, 0, "nonmergeable file needs merge");
error (0, 0, "revision %s from repository is now in %s",
vers->vn_rcs, finfo->fullname);
error (0, 0, "file from working directory is now in %s", backup);
write_letter (finfo->file, 'C', finfo->update_dir);
write_letter (finfo, 'C');
history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
finfo->repository);
@ -1621,7 +1673,7 @@ merge_file (finfo, vers)
goto out;
}
status = RCS_merge(vers->srcfile->path,
status = RCS_merge(finfo->rcs, vers->srcfile->path, finfo->file,
vers->options, vers->vn_user, vers->vn_rcs);
if (status != 0 && status != 1)
{
@ -1684,7 +1736,7 @@ merge_file (finfo, vers)
if (!noexec)
error (0, 0, "conflicts found in %s", finfo->fullname);
write_letter (finfo->file, 'C', finfo->update_dir);
write_letter (finfo, 'C');
history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file, finfo->repository);
@ -1696,7 +1748,7 @@ merge_file (finfo, vers)
}
else
{
write_letter (finfo->file, 'M', finfo->update_dir);
write_letter (finfo, 'M');
history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file,
finfo->repository);
}
@ -1731,14 +1783,6 @@ join_file (finfo, vers)
jdate1 = date_rev1;
jdate2 = date_rev2;
if (wrap_merge_is_copy (finfo->file))
{
error (0, 0,
"Cannot merge %s because it is a merge-by-copy file.",
finfo->fullname);
return;
}
/* Determine if we need to do anything at all. */
if (vers->srcfile == NULL ||
vers->srcfile->path == NULL)
@ -2034,7 +2078,7 @@ join_file (finfo, vers)
"failed to check out %s file", finfo->fullname);
}
#endif
/*
* The users currently modified file is moved to a backup file name
* ".#filename.version", so that it will stay around for a few days
@ -2053,14 +2097,96 @@ join_file (finfo, vers)
xchmod (finfo->file, 1);
options = vers->options;
#ifdef HAVE_RCS5
#if 0
if (*options == '\0')
options = "-kk"; /* to ignore keyword expansions */
#endif
#endif
status = RCS_merge (vers->srcfile->path, options, rev1, rev2);
/* If the source of the merge is the same as the working file
revision, then we can just RCS_checkout the target (no merging
as such). In the text file case, this is probably quite
similar to the RCS_merge, but in the binary file case,
RCS_merge gives all kinds of trouble. */
if (vers->vn_user != NULL
&& strcmp (rev1, vers->vn_user) == 0
/* See comments above about how No_Difference has already been
called. */
&& vers->ts_user != NULL
&& strcmp (vers->ts_user, vers->ts_rcs) == 0
/* This is because of the worry below about $Name. If that
isn't a problem, I suspect this code probably works for
text files too. */
&& (strcmp (options, "-kb") == 0
|| wrap_merge_is_copy (finfo->file)))
{
/* FIXME: what about nametag? What does RCS_merge do with
$Name? */
if (RCS_checkout (finfo->rcs, finfo->file, rev2, NULL, options,
RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0)
status = 2;
else
status = 0;
/* OK, this is really stupid. RCS_checkout carefully removes
write permissions, and we carefully put them back. But
until someone gets around to fixing it, that seems like the
easiest way to get what would seem to be the right mode.
I don't check CVSWRITE or _watched; I haven't thought about
that in great detail, but it seems like a watched file should
be checked out (writable) after a merge. */
xchmod (finfo->file, 1);
/* Traditionally, the text file case prints a whole bunch of
scary looking and verbose output which fails to tell the user
what is really going on (it gives them rev1 and rev2 but doesn't
indicate in any way that rev1 == vn_user). I think just a
simple "U foo" is good here; it seems analogous to the case in
which the file was added on the branch in terms of what to
print. */
write_letter (finfo, 'U');
}
else if (strcmp (options, "-kb") == 0
|| wrap_merge_is_copy (finfo->file))
{
/* We are dealing with binary files, but real merging would
need to take place. This is a conflict. We give the user
the two files, and let them resolve it. It is possible
that we should require a "touch foo" or similar step before
we allow a checkin. */
if (RCS_checkout (finfo->rcs, finfo->file, rev2, NULL, options,
RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0)
status = 2;
else
status = 0;
/* OK, this is really stupid. RCS_checkout carefully removes
write permissions, and we carefully put them back. But
until someone gets around to fixing it, that seems like the
easiest way to get what would seem to be the right mode.
I don't check CVSWRITE or _watched; I haven't thought about
that in great detail, but it seems like a watched file should
be checked out (writable) after a merge. */
xchmod (finfo->file, 1);
/* Hmm. We don't give them REV1 anywhere. I guess most people
probably don't have a 3-way merge tool for the file type in
question, and might just get confused if we tried to either
provide them with a copy of the file from REV1, or even just
told them what REV1 is so they can get it themself, but it
might be worth thinking about. */
/* See comment in merge_file about the "nonmergeable file"
terminology. */
error (0, 0, "nonmergeable file needs merge");
error (0, 0, "revision %s from repository is now in %s",
rev2, finfo->fullname);
error (0, 0, "file from working directory is now in %s", backup);
write_letter (finfo, 'C');
}
else
status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
options, rev1, rev2);
if (status != 0 && status != 1)
{
error (0, status == -1 ? errno : 0,