#include <assert.h>
#include "cvs.h"
#include "savecwd.h"
#include "fileattr.h"
static int add_directory PROTO ((struct file_info *finfo));
static int build_entry PROTO((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\tUse \"rcs-kflag\" to add the file with the specified kflag.\n",
"\t-m\tUse \"message\" for the creation log.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
int
add (argc, argv)
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 (ISDIRSEP (*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_exit ();
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
&& ISDIRSEP (repository[cvsroot_len])
&& strncmp (repository + cvsroot_len + 1,
CVSROOTADM,
sizeof CVSROOTADM - 1) == 0
&& ISDIRSEP (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 = xmalloc (strlen (repository) + strlen (p) + 5);
sprintf (rcsdir, "%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, NULL))
error_exit ();
free_cwd (&cwd);
if (tag)
free (tag);
if (date)
free (date);
free (rcsdir);
if (p == filedir)
Subdir_Register ((List *) NULL, (char *) NULL, argv[j]);
else
{
Subdir_Register ((List *) 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_exit ();
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
&& ISDIRSEP (repository[cvsroot_len])
&& strncmp (repository + cvsroot_len + 1,
CVSROOTADM,
sizeof CVSROOTADM - 1) == 0
&& ISDIRSEP (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 = xmalloc (strlen (repository)
+ strlen (finfo.file)
+ 10);
(void) sprintf (dname, "%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, "illegal 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)
{
if (vers->tag)
error (0, 0,
"file `%s' will be added on branch `%s' from version %s",
finfo.fullname, vers->tag,
vers->vn_rcs);
else
error (0, 0,
"Re-adding file `%s' (in place of dead revision %s).",
finfo.fullname, vers->vn_rcs);
}
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 = xmalloc (strlen (vers->vn_user));
(void) strcpy (tmp, 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, 1);
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, NULL))
error_exit ();
free_cwd (&cwd);
free ((char *) finfo.fullname);
free ((char *) 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 (finfo)
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))
return 1;
if (CVS_CHDIR (dir) < 0)
{
error (0, errno, "cannot chdir to %s", finfo->fullname);
return 1;
}
#ifdef SERVER_SUPPORT
if (!server_active && isfile (CVSADM))
#else
if (isfile (CVSADM))
#endif
{
error (0, 0, "%s/%s already exists", finfo->fullname, CVSADM);
goto out;
}
rcsdir = xmalloc (strlen (repository) + strlen (dir) + 5);
sprintf (rcsdir, "%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 = xmalloc (strlen (rcsdir)
+ 80
+ (tag == NULL ? 0 : strlen (tag) + 80)
+ (date == NULL ? 0 : strlen (date) + 80));
(void) sprintf (message, "Directory %s added to the repository\n",
rcsdir);
if (tag)
{
(void) strcat (message, "--> Using per-directory sticky tag `");
(void) strcat (message, tag);
(void) strcat (message, "'\n");
}
if (date)
{
(void) strcat (message, "--> Using per-directory sticky date `");
(void) strcat (message, date);
(void) strcat (message, "'\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 = (struct logfile_info *) 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, (FILE *) NULL, ulist);
dellist (&ulist);
}
#ifdef SERVER_SUPPORT
if (!server_active)
#endif
Create_Admin (".", finfo->fullname, rcsdir, tag, date, nonbranch, 0, 1);
if (tag)
free (tag);
if (date)
free (date);
if (restore_cwd (&cwd, NULL))
error_exit ();
free_cwd (&cwd);
Subdir_Register (entries, (char *) NULL, dir);
if (!really_quiet)
cvs_output (message, 0);
free (rcsdir);
free (message);
return 0;
out:
if (restore_cwd (&cwd, NULL))
error_exit ();
free_cwd (&cwd);
if (message) free (message);
if (rcsdir != NULL)
free (rcsdir);
return 0;
}
static int
build_entry (repository, user, options, message, entries, tag)
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 = xmalloc (strlen (user) + 80);
(void) sprintf (fname, "%s/%s%s", CVSADM, user, CVSEXT_LOG);
fp = open_file (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 = xmalloc (strlen (user) + 20);
(void) sprintf (line, "Initial %s", user);
Register (entries, user, "0", line, options, tag, (char *) 0, (char *) 0);
free (line);
return 0;
}