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