Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
      3  *
      4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
      5  *                                  and others.
      6  *
      7  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
      8  * Portions Copyright (C) 1989-1992, Brian Berliner
      9  *
     10  * You may distribute under the terms of the GNU General Public License as
     11  * specified in the README file that comes with the CVS source distribution.
     12  *
     13  * Remove a File
     14  *
     15  * Removes entries from the present version. The entries will be removed from
     16  * the RCS repository upon the next "commit".
     17  *
     18  * "remove" accepts no options, only file names that are to be removed.  The
     19  * file must not exist in the current directory for "remove" to work
     20  * correctly.
     21  */
     22 #include <sys/cdefs.h>
     23 __RCSID("$NetBSD: remove.c,v 1.4 2016/05/17 14:00:09 christos Exp $");
     24 
     25 #include "cvs.h"
     26 
     27 #ifdef CLIENT_SUPPORT
     28 static int remove_force_fileproc (void *callerdat,
     29 					 struct file_info *finfo);
     30 #endif
     31 static int remove_fileproc (void *callerdat, struct file_info *finfo);
     32 static Dtype remove_dirproc (void *callerdat, const char *dir,
     33                              const char *repos, const char *update_dir,
     34                              List *entries);
     35 
     36 static int force;
     37 static int local;
     38 static int removed_files;
     39 static int existing_files;
     40 
     41 static const char *const remove_usage[] =
     42 {
     43     "Usage: %s %s [-flR] [files...]\n",
     44     "\t-f\tDelete the file before removing it.\n",
     45     "\t-l\tProcess this directory only (not recursive).\n",
     46     "\t-R\tProcess directories recursively.\n",
     47     "(Specify the --help global option for a list of other help options)\n",
     48     NULL
     49 };
     50 
     51 int
     52 cvsremove (int argc, char **argv)
     53 {
     54     int c, err;
     55 
     56     if (argc == -1)
     57 	usage (remove_usage);
     58 
     59     getoptreset ();
     60     while ((c = getopt (argc, argv, "+flR")) != -1)
     61     {
     62 	switch (c)
     63 	{
     64 	    case 'f':
     65 		force = 1;
     66 		break;
     67 	    case 'l':
     68 		local = 1;
     69 		break;
     70 	    case 'R':
     71 		local = 0;
     72 		break;
     73 	    case '?':
     74 	    default:
     75 		usage (remove_usage);
     76 		break;
     77 	}
     78     }
     79     argc -= optind;
     80     argv += optind;
     81 
     82     wrap_setup ();
     83 
     84 #ifdef CLIENT_SUPPORT
     85     if (current_parsed_root->isremote) {
     86 	/* Call expand_wild so that the local removal of files will
     87            work.  It's ok to do it always because we have to send the
     88            file names expanded anyway.  */
     89 	expand_wild (argc, argv, &argc, &argv);
     90 
     91 	if (force)
     92 	{
     93 	    if (!noexec)
     94 	    {
     95 		start_recursion (remove_force_fileproc, NULL, NULL, NULL,
     96 				 NULL, argc, argv, local, W_LOCAL,
     97 				 0, CVS_LOCK_NONE, NULL, 0, NULL);
     98 	    }
     99 	    /* else FIXME should probably act as if the file doesn't exist
    100 	       in doing the following checks.  */
    101 	}
    102 
    103 	start_server ();
    104 	ign_setup ();
    105 	if (local)
    106 	    send_arg("-l");
    107 	send_arg ("--");
    108 	/* FIXME: Can't we set SEND_NO_CONTENTS here?  Needs investigation.  */
    109 	send_files (argc, argv, local, 0, 0);
    110 	send_file_names (argc, argv, 0);
    111 	free_names (&argc, argv);
    112 	send_to_server ("remove\012", 0);
    113         return get_responses_and_close ();
    114     }
    115 #endif
    116 
    117     /* start the recursion processor */
    118     err = start_recursion (remove_fileproc, NULL, remove_dirproc, NULL,
    119 			   NULL, argc, argv, local, W_LOCAL, 0,
    120 			   CVS_LOCK_READ, NULL, 1, NULL);
    121 
    122     if (removed_files && !really_quiet)
    123 	error (0, 0, "use `%s commit' to remove %s permanently", program_name,
    124 	       (removed_files == 1) ? "this file" : "these files");
    125 
    126     if (existing_files)
    127 	error (0, 0,
    128 	       ((existing_files == 1) ?
    129 		"%d file exists; remove it first" :
    130 		"%d files exist; remove them first"),
    131 	       existing_files);
    132 
    133     return (err);
    134 }
    135 
    136 #ifdef CLIENT_SUPPORT
    137 
    138 /*
    139  * This is called via start_recursion if we are running as the client
    140  * and the -f option was used.  We just physically remove the file.
    141  */
    142 
    143 /*ARGSUSED*/
    144 static int
    145 remove_force_fileproc (void *callerdat, struct file_info *finfo)
    146 {
    147     if (CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno))
    148 	error (0, errno, "unable to remove %s", finfo->fullname);
    149     return 0;
    150 }
    151 
    152 #endif
    153 
    154 /*
    155  * remove the file, only if it has already been physically removed
    156  */
    157 /* ARGSUSED */
    158 static int
    159 remove_fileproc (void *callerdat, struct file_info *finfo)
    160 {
    161     Vers_TS *vers;
    162 
    163     if (force)
    164     {
    165 	if (!noexec)
    166 	{
    167 	    if ( CVS_UNLINK (finfo->file) < 0 && ! existence_error (errno))
    168 	    {
    169 		error (0, errno, "unable to remove %s", finfo->fullname);
    170 	    }
    171 	}
    172 	/* else FIXME should probably act as if the file doesn't exist
    173 	   in doing the following checks.  */
    174     }
    175 
    176     vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
    177 
    178     if (vers->ts_user != NULL)
    179     {
    180 	existing_files++;
    181 	if (!quiet)
    182 	    error (0, 0, "file `%s' still in working directory",
    183 		   finfo->fullname);
    184     }
    185     else if (vers->vn_user == NULL)
    186     {
    187 	if (!quiet)
    188 	    error (0, 0, "nothing known about `%s'", finfo->fullname);
    189     }
    190     else if (vers->vn_user[0] == '0' && vers->vn_user[1] == '\0')
    191     {
    192 	char *fname;
    193 
    194 	/*
    195 	 * It's a file that has been added, but not commited yet. So,
    196 	 * remove the ,t file for it and scratch it from the
    197 	 * entries file.  */
    198 	Scratch_Entry (finfo->entries, finfo->file);
    199 	fname = Xasprintf ("%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
    200 	if (unlink_file (fname) < 0
    201 	    && !existence_error (errno))
    202 	    error (0, errno, "cannot remove %s", CVSEXT_LOG);
    203 	if (!quiet)
    204 	    error (0, 0, "removed `%s'", finfo->fullname);
    205 
    206 #ifdef SERVER_SUPPORT
    207 	if (server_active)
    208 	    server_checked_in (finfo->file, finfo->update_dir, finfo->repository);
    209 #endif
    210 	free (fname);
    211     }
    212     else if (vers->vn_user[0] == '-')
    213     {
    214 	if (!quiet)
    215 	    error (0, 0, "file `%s' already scheduled for removal",
    216 		   finfo->fullname);
    217     }
    218     else if (vers->tag != NULL && isdigit ((unsigned char) *vers->tag))
    219     {
    220 	/* Commit will just give an error, and so there seems to be
    221 	   little reason to allow the remove.  I mean, conflicts that
    222 	   arise out of parallel development are one thing, but conflicts
    223 	   that arise from sticky tags are quite another.
    224 
    225 	   I would have thought that non-branch sticky tags should be the
    226 	   same but at least now, removing a file with a non-branch sticky
    227 	   tag means to delete the tag from the file.  I'm not sure that
    228 	   is a good behavior, but until it is changed, we need to allow
    229 	   it.  */
    230 	error (0, 0, "\
    231 cannot remove file `%s' which has a numeric sticky tag of `%s'",
    232 	       finfo->fullname, vers->tag);
    233     }
    234     else if (vers->date != NULL)
    235     {
    236 	/* Commit will just give an error, and so there seems to be
    237 	   little reason to allow the remove.  */
    238 	error (0, 0, "\
    239 cannot remove file `%s' which has a sticky date of `%s'",
    240 	       finfo->fullname, vers->date);
    241     }
    242     else
    243     {
    244 	char *fname;
    245 
    246 /* cvsacl patch */
    247 #ifdef SERVER_SUPPORT
    248 	if (use_cvs_acl /* && server_active */)
    249 	{
    250 	    if (!access_allowed (finfo->file, finfo->repository, vers->tag, 7,
    251 				 NULL, NULL, 1))
    252 	    {
    253 		if (stop_at_first_permission_denied)
    254 		    error (1, 0, "permission denied for %s",
    255 			   Short_Repository (finfo->repository));
    256 		else
    257 		    error (0, 0, "permission denied for %s/%s",
    258 			   Short_Repository (finfo->repository), finfo->file);
    259 
    260 		return (0);
    261 	    }
    262 	}
    263 #endif
    264 
    265 	/* Re-register it with a negative version number.  */
    266 	fname = Xasprintf ("-%s", vers->vn_user);
    267 	Register (finfo->entries, finfo->file, fname, vers->ts_rcs,
    268 		  vers->options, vers->tag, vers->date, vers->ts_conflict);
    269 	if (!quiet)
    270 	    error (0, 0, "scheduling `%s' for removal", finfo->fullname);
    271 	removed_files++;
    272 
    273 #ifdef SERVER_SUPPORT
    274 	if (server_active)
    275 	    server_checked_in (finfo->file, finfo->update_dir, finfo->repository);
    276 #endif
    277 	free (fname);
    278     }
    279 
    280     freevers_ts (&vers);
    281     return (0);
    282 }
    283 
    284 
    285 
    286 /*
    287  * Print a warm fuzzy message
    288  */
    289 /* ARGSUSED */
    290 static Dtype
    291 remove_dirproc (void *callerdat, const char *dir, const char *repos,
    292                 const char *update_dir, List *entries)
    293 {
    294     if (!quiet)
    295 	error (0, 0, "Removing %s", update_dir);
    296     return (R_PROCESS);
    297 }
    298