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