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