xref: /openbsd/gnu/usr.bin/cvs/src/remove.c (revision d415bd75)
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
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
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
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
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