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