xref: /netbsd-src/external/gpl2/xcvs/dist/src/remove.c (revision 5a6c14c844c4c665da5632061aebde7bb2cb5766)
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
cvsremove(int argc,char ** argv)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
remove_force_fileproc(void * callerdat,struct file_info * finfo)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
remove_fileproc(void * callerdat,struct file_info * finfo)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
remove_dirproc(void * callerdat,const char * dir,const char * repos,const char * update_dir,List * entries)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