#include <assert.h>
#include "cvs.h"
#include "save-cwd.h"
#include "fileattr.h"
static int add_directory (struct file_info *finfo);
static int build_entry (const char *repository, const char *user,
const char *options, const char *message,
List * entries, const char *tag);
static const char *const add_usage[] =
{
"Usage: %s %s [-k rcs-kflag] [-m message] files...\n",
"\t-k rcs-kflag\tUse \"rcs-kflag\" to add the file with the specified\n",
"\t\t\tkflag.\n",
"\t-m message\tUse \"message\" for the creation log.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
int
add (int argc, char **argv)
{
char *message = NULL;
int i;
char *repository;
int c;
int err = 0;
int added_files = 0;
char *options = NULL;
List *entries;
Vers_TS *vers;
struct saved_cwd cwd;
int found_slash = 0;
size_t cvsroot_len;
if (argc == 1 || argc == -1)
usage (add_usage);
wrap_setup ();
optind = 0;
while ((c = getopt (argc, argv, "+k:m:")) != -1)
{
switch (c)
{
case 'k':
if (options) free (options);
options = RCS_check_kflag (optarg);
break;
case 'm':
if (message) free (message);
message = xstrdup (optarg);
break;
case '?':
default:
usage (add_usage);
break;
}
}
argc -= optind;
argv += optind;
if (argc <= 0)
usage (add_usage);
cvsroot_len = strlen (current_parsed_root->directory);
for (i = 0; i < argc; i++)
{
int skip_file = 0;
strip_trailing_slashes (argv[i]);
if (strcmp (argv[i], ".") == 0
|| strcmp (argv[i], "..") == 0
|| fncmp (argv[i], CVSADM) == 0)
{
if (!quiet)
error (0, 0, "cannot add special file `%s'; skipping", argv[i]);
skip_file = 1;
}
else
{
char *p;
p = argv[i];
while (*p != '\0')
{
if (ISSLASH (*p))
{
found_slash = 1;
break;
}
++p;
}
}
if (skip_file)
{
int j;
for (j = i; j < argc - 1; ++j)
argv[j] = argv[j + 1];
--argc;
--i;
++err;
}
}
#ifdef CLIENT_SUPPORT
if (current_parsed_root->isremote)
{
int j;
if (argc == 0)
return err;
start_server ();
ign_setup ();
if (options)
{
send_arg (options);
free (options);
}
option_with_arg ("-m", message);
send_arg ("--");
if (found_slash)
{
repository = Name_Repository (NULL, NULL);
send_a_repository ("", repository, "");
free (repository);
}
for (j = 0; j < argc; ++j)
{
if (isdir (argv[j]))
{
char *tag;
char *date;
int nonbranch;
char *rcsdir;
char *p;
char *update_dir;
char *filedir;
if (save_cwd (&cwd))
error (1, errno, "Failed to save current directory.");
filedir = xstrdup (argv[j]);
p = (char *)last_component (filedir);
if (p == filedir)
{
update_dir = "";
}
else
{
p[-1] = '\0';
update_dir = filedir;
if (CVS_CHDIR (update_dir) < 0)
error (1, errno,
"could not chdir to `%s'", update_dir);
}
repository = Name_Repository (NULL, update_dir);
if (strncmp (repository, current_parsed_root->directory, cvsroot_len) == 0
&& ISSLASH (repository[cvsroot_len])
&& strncmp (repository + cvsroot_len + 1,
CVSROOTADM,
sizeof CVSROOTADM - 1) == 0
&& ISSLASH (repository[cvsroot_len + sizeof CVSROOTADM])
&& strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1,
CVSNULLREPOS) == 0)
error (1, 0, "cannot add to `%s'", repository);
ParseTag (&tag, &date, &nonbranch);
rcsdir = Xasprintf ("%s/%s", repository, p);
Create_Admin (p, argv[j], rcsdir, tag, date,
nonbranch, 0, 1);
if (found_slash)
send_a_repository ("", repository, update_dir);
if (restore_cwd (&cwd))
error (1, errno,
"Failed to restore current directory, `%s'.",
cwd.name);
free_cwd (&cwd);
if (tag)
free (tag);
if (date)
free (date);
free (rcsdir);
if (p == filedir)
Subdir_Register (NULL, NULL, argv[j]);
else
{
Subdir_Register (NULL, update_dir, p);
}
free (repository);
free (filedir);
}
}
send_files (argc, argv, 0, 0, SEND_BUILD_DIRS | SEND_NO_CONTENTS);
send_file_names (argc, argv, SEND_EXPAND_WILD);
send_to_server ("add\012", 0);
if (message)
free (message);
return err + get_responses_and_close ();
}
#endif
for (i = 0; i < argc; i++)
{
int begin_err = err;
#ifdef SERVER_SUPPORT
int begin_added_files = added_files;
#endif
struct file_info finfo;
char *filename, *p;
memset (&finfo, 0, sizeof finfo);
if (save_cwd (&cwd))
error (1, errno, "Failed to save current directory.");
finfo.fullname = xstrdup (argv[i]);
filename = xstrdup (argv[i]);
p = (char *)last_component (filename);
if (p == filename)
{
finfo.update_dir = "";
finfo.file = p;
}
else
{
p[-1] = '\0';
finfo.update_dir = filename;
finfo.file = p;
if (CVS_CHDIR (finfo.update_dir) < 0)
error (1, errno, "could not chdir to `%s'", finfo.update_dir);
}
wrap_add_file (CVSDOTWRAPPER, 1);
finfo.rcs = NULL;
repository = Name_Repository (NULL, finfo.update_dir);
if (strncmp (repository, current_parsed_root->directory,
cvsroot_len) == 0
&& ISSLASH (repository[cvsroot_len])
&& strncmp (repository + cvsroot_len + 1,
CVSROOTADM,
sizeof CVSROOTADM - 1) == 0
&& ISSLASH (repository[cvsroot_len + sizeof CVSROOTADM])
&& strcmp (repository + cvsroot_len + sizeof CVSROOTADM + 1,
CVSNULLREPOS) == 0)
error (1, 0, "cannot add to `%s'", repository);
entries = Entries_Open (0, NULL);
finfo.repository = repository;
finfo.entries = entries;
vers = Version_TS (&finfo, options, NULL, NULL, 1, 0);
if (vers->vn_user == NULL)
{
if (vers->vn_rcs == NULL)
{
if (vers->ts_user == NULL)
{
error (0, 0, "nothing known about `%s'", finfo.fullname);
err++;
}
else if (!isdir (finfo.file)
|| wrap_name_has (finfo.file, WRAP_TOCVS))
{
char *dname = Xasprintf ("%s/%s", repository, finfo.file);
if (isdir (dname))
{
error (0, 0,
"cannot add file `%s' since the directory",
finfo.fullname);
error (0, 0, "`%s' already exists in the repository",
dname);
error (1, 0, "invalid filename overlap");
}
free (dname);
if (vers->options == NULL || *vers->options == '\0')
{
if (wrap_name_has (finfo.file, WRAP_RCSOPTION))
{
if (vers->options)
free (vers->options);
vers->options = wrap_rcsoption (finfo.file, 1);
}
}
if (vers->nonbranch)
{
error (0, 0,
"cannot add file on non-branch tag `%s'",
vers->tag);
++err;
}
else
{
if (build_entry (repository, finfo.file, vers->options,
message, entries, vers->tag) != 0)
err++;
else
{
added_files++;
if (!quiet)
{
if (vers->tag)
error (0, 0, "scheduling %s `%s' for"
" addition on branch `%s'",
(wrap_name_has (finfo.file,
WRAP_TOCVS)
? "wrapper"
: "file"),
finfo.fullname, vers->tag);
else
error (0, 0,
"scheduling %s `%s' for addition",
(wrap_name_has (finfo.file,
WRAP_TOCVS)
? "wrapper"
: "file"),
finfo.fullname);
}
}
}
}
}
else if (RCS_isdead (vers->srcfile, vers->vn_rcs))
{
if (isdir (finfo.file)
&& !wrap_name_has (finfo.file, WRAP_TOCVS))
{
error (0, 0,
"the directory `%s' cannot be added because a file"
" of the", finfo.fullname);
error (1, 0, "same name already exists in the repository.");
}
else
{
if (vers->nonbranch)
{
error (0, 0,
"cannot add file on non-branch tag `%s'",
vers->tag);
++err;
}
else
{
char *timestamp = NULL;
if (vers->ts_user == NULL)
{
char *prev = previous_rev (vers->srcfile,
vers->vn_rcs);
int status;
if (prev == NULL)
{
if (!really_quiet)
error (0, 0,
"File `%s' has no previous revision to resurrect.",
finfo.fullname);
free (prev);
goto skip_this_file;
}
if (!quiet)
error (0, 0,
"Resurrecting file `%s' from revision %s.",
finfo.fullname, prev);
status = RCS_checkout (vers->srcfile, finfo.file,
prev, vers->tag,
vers->options, RUN_TTY,
NULL, NULL);
xchmod (finfo.file, 1);
if (status != 0)
{
error (0, 0, "Failed to resurrect revision %s",
prev);
err++;
}
else
{
timestamp = time_stamp (finfo.file);
if (!really_quiet)
write_letter (&finfo, 'U');
}
free (prev);
}
if (!quiet)
{
char *bbuf;
if (vers->tag)
{
bbuf = Xasprintf (" on branch `%s'",
vers->tag);
}
else
bbuf = "";
error (0, 0,
"Re-adding file `%s'%s after dead revision %s.",
finfo.fullname, bbuf, vers->vn_rcs);
if (vers->tag)
free (bbuf);
}
Register (entries, finfo.file, "0",
timestamp ? timestamp : vers->ts_user,
vers->options, vers->tag, vers->date, NULL);
if (timestamp) free (timestamp);
#ifdef SERVER_SUPPORT
if (server_active && vers->ts_user == NULL)
{
server_updated (&finfo, vers,
SERVER_UPDATED,
(mode_t) -1, NULL, NULL);
++begin_added_files;
}
#endif
++added_files;
}
}
}
else
{
error (0, 0, "`%s' added independently by second party",
finfo.fullname);
err++;
}
}
else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
{
if (!quiet)
error (0, 0, "`%s' has already been entered", finfo.fullname);
err++;
}
else if (vers->vn_user[0] == '-')
{
if (vers->ts_user == NULL)
{
if (vers->vn_rcs == NULL)
{
error (0, 0,
"cannot resurrect `%s'; RCS file removed by"
" second party", finfo.fullname);
err++;
}
else
{
int status;
char *tmp = xstrdup (vers->vn_user + 1);
(void) strcpy (vers->vn_user, tmp);
free (tmp);
status = RCS_checkout (vers->srcfile, finfo.file,
vers->vn_user, vers->tag,
vers->options, RUN_TTY,
NULL, NULL);
xchmod (finfo.file, cvswrite);
if (status != 0)
{
error (0, 0, "Failed to resurrect revision %s.",
vers->vn_user);
err++;
tmp = NULL;
}
else
{
tmp = time_stamp (finfo.file);
write_letter (&finfo, 'U');
if (!quiet)
error (0, 0, "`%s', version %s, resurrected",
finfo.fullname, vers->vn_user);
}
Register (entries, finfo.file, vers->vn_user,
tmp, vers->options,
vers->tag, vers->date, NULL);
if (tmp) free (tmp);
#ifdef SERVER_SUPPORT
if (server_active)
{
server_updated (&finfo, vers,
SERVER_UPDATED,
(mode_t) -1, NULL, NULL);
}
#endif
}
}
else
{
error (0, 0, "\
`%s' should be removed and is still there (or is back again)", finfo.fullname);
err++;
}
}
else
{
if (!quiet)
error (0, 0, "`%s' already exists, with version number %s",
finfo.fullname,
vers->vn_user);
err++;
}
freevers_ts (&vers);
if (begin_err == err
&& isdir (finfo.file)
&& !wrap_name_has (finfo.file, WRAP_TOCVS))
{
err += add_directory (&finfo);
}
else
{
#ifdef SERVER_SUPPORT
if (server_active && begin_added_files != added_files)
server_checked_in (finfo.file, finfo.update_dir, repository);
#endif
}
skip_this_file:
free (repository);
Entries_Close (entries);
if (restore_cwd (&cwd))
error (1, errno, "Failed to restore current directory, `%s'.",
cwd.name);
free_cwd (&cwd);
free ((char *) finfo.fullname);
free (filename);
}
if (added_files && !really_quiet)
error (0, 0, "use `%s commit' to add %s permanently",
program_name,
(added_files == 1) ? "this file" : "these files");
if (message)
free (message);
if (options)
free (options);
return err;
}
static int
add_directory (struct file_info *finfo)
{
const char *repository = finfo->repository;
List *entries = finfo->entries;
const char *dir = finfo->file;
char *rcsdir = NULL;
struct saved_cwd cwd;
char *message = NULL;
char *tag, *date;
int nonbranch;
char *attrs;
if (strchr (dir, '/') != NULL)
{
error (0, 0,
"directory %s not added; must be a direct sub-directory", dir);
return 1;
}
if (fncmp (dir, CVSADM) == 0)
{
error (0, 0, "cannot add a `%s' directory", CVSADM);
return 1;
}
ParseTag (&tag, &date, &nonbranch);
fileattr_startdir (repository);
attrs = fileattr_getall (NULL);
fileattr_free ();
if (save_cwd (&cwd))
{
error (0, errno, "Failed to save current directory.");
return 1;
}
if (CVS_CHDIR (dir) < 0)
{
error (0, errno, "cannot chdir to %s", finfo->fullname);
return 1;
}
if (!server_active && isfile (CVSADM))
{
error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM);
goto out;
}
rcsdir = Xasprintf ("%s/%s", repository, dir);
if (isfile (rcsdir) && !isdir (rcsdir))
{
error (0, 0, "%s is not a directory; %s not added", rcsdir,
finfo->fullname);
goto out;
}
message = Xasprintf ("Directory %s added to the repository\n%s%s%s%s%s%s",
rcsdir,
tag ? "--> Using per-directory sticky tag `" : "",
tag ? tag : "", tag ? "'\n" : "",
date ? "--> Using per-directory sticky date `" : "",
date ? date : "", date ? "'\n" : "");
if (!isdir (rcsdir))
{
mode_t omask;
Node *p;
List *ulist;
struct logfile_info *li;
if (!noexec)
{
omask = umask (cvsumask);
if (CVS_MKDIR (rcsdir, 0777) < 0)
{
error (0, errno, "cannot mkdir %s", rcsdir);
(void) umask (omask);
goto out;
}
(void) umask (omask);
}
fileattr_startdir (rcsdir);
fileattr_setall (NULL, attrs);
fileattr_write ();
fileattr_free ();
if (attrs != NULL)
free (attrs);
ulist = getlist ();
p = getnode ();
p->type = UPDATE;
p->delproc = update_delproc;
p->key = xstrdup ("- New directory");
li = xmalloc (sizeof (struct logfile_info));
li->type = T_TITLE;
li->tag = xstrdup (tag);
li->rev_old = li->rev_new = NULL;
p->data = li;
(void) addnode (ulist, p);
Update_Logfile (rcsdir, message, NULL, ulist);
dellist (&ulist);
}
if (server_active)
WriteTemplate (finfo->fullname, 1, rcsdir);
else
Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0, 1);
if (tag)
free (tag);
if (date)
free (date);
if (restore_cwd (&cwd))
error (1, errno, "Failed to restore current directory, `%s'.",
cwd.name);
free_cwd (&cwd);
Subdir_Register (entries, NULL, dir);
if (!really_quiet)
cvs_output (message, 0);
free (rcsdir);
free (message);
return 0;
out:
if (restore_cwd (&cwd))
error (1, errno, "Failed to restore current directory, `%s'.",
cwd.name);
free_cwd (&cwd);
if (message) free (message);
if (rcsdir != NULL)
free (rcsdir);
return 0;
}
static int
build_entry (const char *repository, const char *user, const char *options,
const char *message, List *entries, const char *tag)
{
char *fname;
char *line;
FILE *fp;
if (noexec)
return 0;
fname = Xasprintf ("%s/%s%s", CVSADM, user, CVSEXT_LOG);
fp = xfopen (fname, "w+");
if (message && fputs (message, fp) == EOF)
error (1, errno, "cannot write to %s", fname);
if (fclose (fp) == EOF)
error (1, errno, "cannot close %s", fname);
free (fname);
line = Xasprintf ("Initial %s", user);
Register (entries, user, "0", line, options, tag, NULL, NULL);
free (line);
return 0;
}