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