1 /*
2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
4 *
5 * You may distribute under the terms of the GNU General Public License as
6 * specified in the README file that comes with the CVS source distribution.
7 *
8 * Remove a File
9 *
10 * Removes entries from the present version. The entries will be removed from
11 * the RCS repository upon the next "commit".
12 *
13 * "remove" accepts no options, only file names that are to be removed. The
14 * file must not exist in the current directory for "remove" to work
15 * correctly.
16 */
17
18 #include "cvs.h"
19
20 #ifdef CLIENT_SUPPORT
21 static int remove_force_fileproc PROTO ((void *callerdat,
22 struct file_info *finfo));
23 #endif
24 static int remove_fileproc PROTO ((void *callerdat, struct file_info *finfo));
25 static Dtype remove_dirproc PROTO ((void *callerdat, char *dir,
26 char *repos, char *update_dir,
27 List *entries));
28
29 static int force;
30 static int local;
31 static int removed_files;
32 static int existing_files;
33
34 static const char *const remove_usage[] =
35 {
36 "Usage: %s %s [-flR] [files...]\n",
37 "\t-f\tDelete the file before removing it.\n",
38 "\t-l\tProcess this directory only (not recursive).\n",
39 "\t-R\tProcess directories recursively.\n",
40 "(Specify the --help global option for a list of other help options)\n",
41 NULL
42 };
43
44 int
cvsremove(argc,argv)45 cvsremove (argc, argv)
46 int argc;
47 char **argv;
48 {
49 int c, err;
50
51 if (argc == -1)
52 usage (remove_usage);
53
54 optind = 0;
55 while ((c = getopt (argc, argv, "+flR")) != -1)
56 {
57 switch (c)
58 {
59 case 'f':
60 force = 1;
61 break;
62 case 'l':
63 local = 1;
64 break;
65 case 'R':
66 local = 0;
67 break;
68 case '?':
69 default:
70 usage (remove_usage);
71 break;
72 }
73 }
74 argc -= optind;
75 argv += optind;
76
77 wrap_setup ();
78
79 #ifdef CLIENT_SUPPORT
80 if (current_parsed_root->isremote) {
81 /* Call expand_wild so that the local removal of files will
82 work. It's ok to do it always because we have to send the
83 file names expanded anyway. */
84 expand_wild (argc, argv, &argc, &argv);
85
86 if (force)
87 {
88 if (!noexec)
89 {
90 start_recursion (remove_force_fileproc, (FILESDONEPROC) NULL,
91 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL,
92 (void *) NULL, argc, argv, local, W_LOCAL,
93 0, 0, (char *) NULL, 0);
94 }
95 /* else FIXME should probably act as if the file doesn't exist
96 in doing the following checks. */
97 }
98
99 start_server ();
100 ign_setup ();
101 if (local)
102 send_arg("-l");
103 /* FIXME: Can't we set SEND_NO_CONTENTS here? Needs investigation. */
104 send_files (argc, argv, local, 0, 0);
105 send_file_names (argc, argv, 0);
106 free_names (&argc, argv);
107 send_to_server ("remove\012", 0);
108 return get_responses_and_close ();
109 }
110 #endif
111
112 /* start the recursion processor */
113 err = start_recursion (remove_fileproc, (FILESDONEPROC) NULL,
114 remove_dirproc, (DIRLEAVEPROC) NULL, NULL,
115 argc, argv,
116 local, W_LOCAL, 0, 1, (char *) NULL, 1);
117
118 if (removed_files && !really_quiet)
119 error (0, 0, "use '%s commit' to remove %s permanently", program_name,
120 (removed_files == 1) ? "this file" : "these files");
121
122 if (existing_files)
123 error (0, 0,
124 ((existing_files == 1) ?
125 "%d file exists; remove it first" :
126 "%d files exist; remove them first"),
127 existing_files);
128
129 return (err);
130 }
131
132 #ifdef CLIENT_SUPPORT
133
134 /*
135 * This is called via start_recursion if we are running as the client
136 * and the -f option was used. We just physically remove the file.
137 */
138
139 /*ARGSUSED*/
140 static int
remove_force_fileproc(callerdat,finfo)141 remove_force_fileproc (callerdat, finfo)
142 void *callerdat;
143 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(callerdat,finfo)157 remove_fileproc (callerdat, finfo)
158 void *callerdat;
159 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 = xmalloc (strlen (finfo->file)
200 + sizeof (CVSADM)
201 + sizeof (CVSEXT_LOG)
202 + 10);
203 (void) sprintf (fname, "%s/%s%s", CVSADM, finfo->file, CVSEXT_LOG);
204 if (unlink_file (fname) < 0
205 && !existence_error (errno))
206 error (0, errno, "cannot remove %s", CVSEXT_LOG);
207 if (!quiet)
208 error (0, 0, "removed `%s'", finfo->fullname);
209
210 #ifdef SERVER_SUPPORT
211 if (server_active)
212 server_checked_in (finfo->file, finfo->update_dir, finfo->repository);
213 #endif
214 free (fname);
215 }
216 else if (vers->vn_user[0] == '-')
217 {
218 if (!quiet)
219 error (0, 0, "file `%s' already scheduled for removal",
220 finfo->fullname);
221 }
222 else if (vers->tag != NULL && isdigit ((unsigned char) *vers->tag))
223 {
224 /* Commit will just give an error, and so there seems to be
225 little reason to allow the remove. I mean, conflicts that
226 arise out of parallel development are one thing, but conflicts
227 that arise from sticky tags are quite another.
228
229 I would have thought that non-branch sticky tags should be the
230 same but at least now, removing a file with a non-branch sticky
231 tag means to delete the tag from the file. I'm not sure that
232 is a good behavior, but until it is changed, we need to allow
233 it. */
234 error (0, 0, "\
235 cannot remove file `%s' which has a numeric sticky tag of `%s'",
236 finfo->fullname, vers->tag);
237 }
238 else
239 {
240 char *fname;
241
242 /* Re-register it with a negative version number. */
243 fname = xmalloc (strlen (vers->vn_user) + 5);
244 (void) strcpy (fname, "-");
245 (void) strcat (fname, vers->vn_user);
246 Register (finfo->entries, finfo->file, fname, vers->ts_rcs, vers->options,
247 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 * Print a warm fuzzy message
265 */
266 /* ARGSUSED */
267 static Dtype
remove_dirproc(callerdat,dir,repos,update_dir,entries)268 remove_dirproc (callerdat, dir, repos, update_dir, entries)
269 void *callerdat;
270 char *dir;
271 char *repos;
272 char *update_dir;
273 List *entries;
274 {
275 if (!quiet)
276 error (0, 0, "Removing %s", update_dir);
277 return (R_PROCESS);
278 }
279