/*
* Release: "cancel" a checkout in the history log.
*
* - Enter a line in the history log indicating the "release". - If asked to,
* delete the local working directory.
*/
#include "cvs.h"
#include "savecwd.h"
#include "getline.h"
static int modified_files;
static const char *const release_usage[] =
{
"Usage: %s %s [-d [-f]] [-e] [-y] directories...\n",
"\t-d\tDelete the given directory.\n",
"\t-f\tDelete contents of directories including non-cvs files.\n",
"\t-e\tDelete CVS control files in the given directory (export).\n",
"\t-y\tAssume answer yes to all questions.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
static short force_delete = 0;
#ifdef SERVER_SUPPORT
/* This is the server side of cvs release. */
static int release_server (int argc, char **argv)
{
int i;
/* Note that we skip argv[0]. */
for (i = 1; i < argc; ++i)
history_write ('F', argv[i], "", argv[i], "", NULL, NULL);
return 0;
}
#endif /* SERVER_SUPPORT */
/*ARGSUSED*/
static int release_fileproc (void *callerdat, struct file_info *finfo)
{
int status, q;
q=really_quiet;
really_quiet = 1;
status = Classify_File (finfo, (char *) NULL, (char *) NULL, (char *) NULL,
1, 0, NULL, 0, 0, 0);
really_quiet = q;
switch (status)
{
case T_NEEDS_MERGE:
case T_CONFLICT:
case T_MODIFIED:
case T_ADDED:
case T_REMOVED:
modified_files++;
break;
case T_CHECKOUT:
case T_REMOVE_ENTRY:
case T_UNKNOWN:
case T_UPTODATE:
case T_PATCH:
break;
}
return 0;
}
/*ARGSUSED*/
static int release_delete_fileproc (void *callerdat, struct file_info *finfo)
{
unlink_file(finfo->file);
return 0;
}
/* ARGSUSED */
static int
release_delete_dirleaveproc(void *callerdat, char *dir, int err, char *update_dir, List *entries)
{
if (strchr (dir, '/') == NULL)
{
/* FIXME: chdir ("..") loses with symlinks. */
/* Prune empty dirs on the way out - if necessary */
unlink_file_dir(CVSADM);
(void) CVS_CHDIR ("..");
if (force_delete || isemptydir (dir, 0))
{
List *list;
/* I'm not sure the existence_error is actually possible (except
in cases where we really should print a message), but since
this code used to ignore all errors, I'll play it safe. */
if (unlink_file_dir (dir) < 0 && !existence_error (errno))
error (0, errno, "cannot remove %s directory", dir);
if(isdir(CVSADM))
{
list = Entries_Open(0,NULL);
Subdir_Deregister (list, (char *) NULL, dir);
Entries_Close(list);
}
}
}
return (err);
}
static int release_export_dirleaveproc(void *callerdat, char *dir, int err,
char *update_dir, List *entries)
{
unlink_file_dir(CVSADM);
return 0;
}
int release (int argc, char **argv)
{
int i, c;
char *repository;
char *thisarg;
int arg_start_idx;
int err = 0;
short delete_flag = 0;
short export_flag = 0;
short yes_flag=0;
struct saved_cwd cwd;
#ifdef SERVER_SUPPORT
if (server_active)
return release_server (argc, argv);
#endif
/* Everything from here on is client or local. */
if (argc == -1)
usage (release_usage);
optind = 0;
while ((c = getopt (argc, argv, "+Qdeqfy")) != -1)
{
switch (c)
{
case 'Q':
case 'q':
error (1, 0,
"-q or -Q must be specified before \"%s\"",
command_name);
break;
case 'd':
delete_flag++;
break;
case 'f':
force_delete++;
break;
case 'e':
export_flag++;
break;
case 'y':
yes_flag=1;
break;
case '?':
default:
usage (release_usage);
break;
}
}
argc -= optind;
argv += optind;
/* Remember the directory where "cvs release" was invoked because
all args are relative to this directory and we chdir around.
*/
if (save_cwd (&cwd))
error_exit ();
arg_start_idx = 0;
for (i = arg_start_idx; i < argc; i++)
{
thisarg = argv[i];
if (isdir (thisarg))
{
if (CVS_CHDIR (thisarg) < 0)
{
if (!really_quiet)
error (0, errno, "can't chdir to: %s", thisarg);
continue;
}
if (!isdir (CVSADM))
{
if (!really_quiet)
error (0, 0, "no repository directory: %s", thisarg);
if (restore_cwd (&cwd, NULL))
error_exit ();
continue;
}
}
else
{
if (!really_quiet)
error (0, 0, "no such directory: %s", thisarg);
continue;
}
repository = Name_Repository ((char *) NULL, (char *) NULL);
if (!really_quiet)
{
char *tmp;
modified_files = 0;
start_recursion (release_fileproc, (FILESDONEPROC) NULL,
(PREDIRENTPROC) NULL, (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
(void *) NULL, 0, NULL, 0, W_LOCAL,
0, 0, (char *) NULL, NULL, 0, NULL);
tmp=(char*)xmalloc(strlen(thisarg)+1024);
if(modified_files)
sprintf (tmp,"You have [%d] altered files in this repository.\n", modified_files);
else
*tmp='\0';
sprintf(tmp+strlen(tmp),"Are you sure you want to release %sdirectory '%s': ",
delete_flag ? "(and delete) " : export_flag?"(and export) " : "", thisarg);
if(!yes_flag)
c=yesno_prompt(tmp,"Modified files",0);
else
printf("%sy\n",tmp);
xfree(tmp);
if (c!='y') /* "No" */
{
(void) fprintf (stderr, "** `%s' aborted by user choice.\n",
command_name);
xfree (repository);
if (restore_cwd (&cwd, NULL))
error_exit ();
continue;
}
}
if (!(current_parsed_root->isremote
&& (!supported_request ("noop")
|| !supported_request ("Notify")))
)
{
/* We are chdir'ed into the directory in question.
So don't pass args to unedit. */
int argc = 1;
char *argv[3];
argv[0] = "dummy";
argv[1] = NULL;
err += unedit (argc, argv);
/* Unedit will have killed our lockserver connection */
if(!current_parsed_root->isremote)
lock_register_client(CVS_Username,current_parsed_root->directory);
}
if (current_parsed_root->isremote)
{
send_to_server ("Argument ", 0);
send_to_server (thisarg, 0);
send_to_server ("\n", 1);
send_to_server ("release\n", 0);
}
else
{
history_write ('F', thisarg, "", thisarg, "", NULL, NULL); /* F == Free */
}
xfree (repository);
if (restore_cwd (&cwd, NULL))
error_exit ();
if(!noexec)
{
if(delete_flag)
{
start_recursion (release_delete_fileproc, (FILESDONEPROC) NULL,
(PREDIRENTPROC) NULL, (DIRENTPROC) NULL, release_delete_dirleaveproc,
(void *) NULL, 1, &thisarg, 0, W_LOCAL,
0, 0, (char *) NULL, NULL, 0, NULL);
}
else if(export_flag)
{
start_recursion (NULL, (FILESDONEPROC) NULL,
(PREDIRENTPROC) NULL, (DIRENTPROC) NULL, release_export_dirleaveproc,
(void *) NULL, 1, &thisarg, 0, W_LOCAL,
0, 0, (char *) NULL, NULL, 0, NULL);
}
}
if (current_parsed_root->isremote)
err += get_server_responses ();
}
if (restore_cwd (&cwd, NULL))
error_exit ();
free_cwd (&cwd);
if (current_parsed_root->isremote)
{
/* Unfortunately, client.c doesn't offer a way to close
the connection without waiting for responses. The extra
network turnaround here is quite unnecessary other than
that.... */
send_to_server ("noop\n", 0);
err += get_responses_and_close ();
}
return err;
}
syntax highlighted by Code2HTML, v. 0.9.1