1 /* 2 * Copyright (C) 1994-2005 The Free Software Foundation, Inc. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2, or (at your option) 7 * any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15 /* 16 * Release: "cancel" a checkout in the history log. 17 * 18 * - Enter a line in the history log indicating the "release". - If asked to, 19 * delete the local working directory. 20 */ 21 22 #include "cvs.h" 23 #include "save-cwd.h" 24 #include "getline.h" 25 #include "yesno.h" 26 27 static const char *const release_usage[] = 28 { 29 "Usage: %s %s [-d] directories...\n", 30 "\t-d\tDelete the given directory.\n", 31 "(Specify the --help global option for a list of other help options)\n", 32 NULL 33 }; 34 35 #ifdef SERVER_SUPPORT 36 static int release_server (int argc, char **argv); 37 38 /* This is the server side of cvs release. */ 39 static int 40 release_server (int argc, char **argv) 41 { 42 int i; 43 44 /* Note that we skip argv[0]. */ 45 for (i = 1; i < argc; ++i) 46 history_write ('F', argv[i], "", argv[i], ""); 47 return 0; 48 } 49 50 #endif /* SERVER_SUPPORT */ 51 52 /* Construct an update command. Be sure to add authentication and 53 * encryption if we are using them currently, else our child process may 54 * not be able to communicate with the server. 55 */ 56 static FILE * 57 setup_update_command (pid_t *child_pid) 58 { 59 int tofd, fromfd; 60 61 run_setup (program_path); 62 63 #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) 64 /* Be sure to add authentication and encryption if we are using them 65 * currently, else our child process may not be able to communicate with 66 * the server. 67 */ 68 if (cvsauthenticate) run_add_arg ("-a"); 69 if (cvsencrypt) run_add_arg ("-x"); 70 #endif 71 72 /* Don't really change anything. */ 73 run_add_arg ("-n"); 74 75 /* Don't need full verbosity. */ 76 run_add_arg ("-q"); 77 78 /* Propogate our CVSROOT. */ 79 run_add_arg ("-d"); 80 run_add_arg (original_parsed_root->original); 81 82 run_add_arg ("update"); 83 *child_pid = run_piped (&tofd, &fromfd); 84 if (*child_pid < 0) 85 error (1, 0, "could not fork server process"); 86 87 close (tofd); 88 89 return fdopen (fromfd, "r"); 90 } 91 92 93 94 static int 95 close_update_command (FILE *fp, pid_t child_pid) 96 { 97 int status; 98 pid_t w; 99 100 do 101 w = waitpid (child_pid, &status, 0); 102 while (w == -1 && errno == EINTR); 103 if (w == -1) 104 error (1, errno, "waiting for process %d", child_pid); 105 106 return status; 107 } 108 109 110 111 /* There are various things to improve about this implementation: 112 113 1. Using run_popen to run "cvs update" could be replaced by a 114 fairly simple start_recursion/classify_file loop--a win for 115 portability, performance, and cleanliness. In particular, there is 116 no particularly good way to find the right "cvs". 117 118 2. The fact that "cvs update" contacts the server slows things down; 119 it undermines the case for using "cvs release" rather than "rm -rf". 120 However, for correctly printing "? foo" and correctly handling 121 CVSROOTADM_IGNORE, we currently need to contact the server. (One 122 idea for how to fix this is to stash a copy of CVSROOTADM_IGNORE in 123 the working directories; see comment at base_* in entries.c for a 124 few thoughts on that). 125 126 3. Would be nice to take processing things on the client side one step 127 further, and making it like edit/unedit in terms of working well if 128 disconnected from the network, and then sending a delayed 129 notification. 130 131 4. Having separate network turnarounds for the "Notify" request 132 which we do as part of unedit, and for the "release" itself, is slow 133 and unnecessary. */ 134 135 int 136 release (int argc, char **argv) 137 { 138 FILE *fp; 139 int i, c; 140 char *line = NULL; 141 size_t line_allocated = 0; 142 char *thisarg; 143 int arg_start_idx; 144 int err = 0; 145 short delete_flag = 0; 146 struct saved_cwd cwd; 147 148 #ifdef SERVER_SUPPORT 149 if (server_active) 150 return release_server (argc, argv); 151 #endif 152 153 /* Everything from here on is client or local. */ 154 if (argc == -1) 155 usage (release_usage); 156 optind = 0; 157 while ((c = getopt (argc, argv, "+Qdq")) != -1) 158 { 159 switch (c) 160 { 161 case 'Q': 162 case 'q': 163 error (1, 0, 164 "-q or -Q must be specified before \"%s\"", 165 cvs_cmd_name); 166 break; 167 case 'd': 168 delete_flag++; 169 break; 170 case '?': 171 default: 172 usage (release_usage); 173 break; 174 } 175 } 176 argc -= optind; 177 argv += optind; 178 179 /* We're going to run "cvs -n -q update" and check its output; if 180 * the output is sufficiently unalarming, then we release with no 181 * questions asked. Else we prompt, then maybe release. 182 * (Well, actually we ask no matter what. Our notion of "sufficiently 183 * unalarming" doesn't take into account "? foo.c" files, so it is 184 * up to the user to take note of them, at least currently 185 * (ignore-193 in testsuite)). 186 */ 187 188 #ifdef CLIENT_SUPPORT 189 /* Start the server; we'll close it after looping. */ 190 if (current_parsed_root->isremote) 191 { 192 start_server (); 193 ign_setup (); 194 } 195 #endif /* CLIENT_SUPPORT */ 196 197 /* Remember the directory where "cvs release" was invoked because 198 all args are relative to this directory and we chdir around. 199 */ 200 if (save_cwd (&cwd)) 201 error (1, errno, "Failed to save current directory."); 202 203 arg_start_idx = 0; 204 205 for (i = arg_start_idx; i < argc; i++) 206 { 207 thisarg = argv[i]; 208 209 if (isdir (thisarg)) 210 { 211 if (CVS_CHDIR (thisarg) < 0) 212 { 213 if (!really_quiet) 214 error (0, errno, "can't chdir to: %s", thisarg); 215 continue; 216 } 217 if (!isdir (CVSADM)) 218 { 219 if (!really_quiet) 220 error (0, 0, "no repository directory: %s", thisarg); 221 if (restore_cwd (&cwd)) 222 error (1, errno, 223 "Failed to restore current directory, `%s'.", 224 cwd.name); 225 continue; 226 } 227 } 228 else 229 { 230 if (!really_quiet) 231 error (0, 0, "no such directory: %s", thisarg); 232 continue; 233 } 234 235 if (!really_quiet) 236 { 237 int line_length, status; 238 pid_t child_pid; 239 240 /* The "release" command piggybacks on "update", which 241 does the real work of finding out if anything is not 242 up-to-date with the repository. Then "release" prompts 243 the user, telling her how many files have been 244 modified, and asking if she still wants to do the 245 release. */ 246 fp = setup_update_command (&child_pid); 247 if (fp == NULL) 248 { 249 error (0, 0, "cannot run command:"); 250 run_print (stderr); 251 error (1, 0, "Exiting due to fatal error referenced above."); 252 } 253 254 c = 0; 255 256 while ((line_length = getline (&line, &line_allocated, fp)) >= 0) 257 { 258 if (strchr ("MARCZ", *line)) 259 c++; 260 (void) fputs (line, stdout); 261 } 262 if (line_length < 0 && !feof (fp)) 263 error (0, errno, "cannot read from subprocess"); 264 265 /* If the update exited with an error, then we just want to 266 complain and go on to the next arg. Especially, we do 267 not want to delete the local copy, since it's obviously 268 not what the user thinks it is. */ 269 status = close_update_command (fp, child_pid); 270 if (status != 0) 271 { 272 error (0, 0, "unable to release `%s' (%d)", thisarg, status); 273 if (restore_cwd (&cwd)) 274 error (1, errno, 275 "Failed to restore current directory, `%s'.", 276 cwd.name); 277 continue; 278 } 279 280 printf ("You have [%d] altered files in this repository.\n", 281 c); 282 283 if (!noexec) 284 { 285 printf ("Are you sure you want to release %sdirectory `%s': ", 286 delete_flag ? "(and delete) " : "", thisarg); 287 fflush (stderr); 288 fflush (stdout); 289 if (!yesno ()) /* "No" */ 290 { 291 (void) fprintf (stderr, 292 "** `%s' aborted by user choice.\n", 293 cvs_cmd_name); 294 if (restore_cwd (&cwd)) 295 error (1, errno, 296 "Failed to restore current directory, `%s'.", 297 cwd.name); 298 continue; 299 } 300 } 301 else 302 { 303 if (restore_cwd (&cwd)) 304 error (1, errno, 305 "Failed to restore current directory, `%s'.", 306 cwd.name); 307 continue; 308 } 309 } 310 311 /* Note: client.c doesn't like to have other code 312 changing the current directory on it. So a fair amount 313 of effort is needed to make sure it doesn't get confused 314 about the directory and (for example) overwrite 315 CVS/Entries file in the wrong directory. See release-17 316 through release-23. */ 317 318 if (restore_cwd (&cwd)) 319 error (1, errno, "Failed to restore current directory, `%s'.", 320 cwd.name); 321 322 #ifdef CLIENT_SUPPORT 323 if (!current_parsed_root->isremote 324 || (supported_request ("noop") && supported_request ("Notify"))) 325 #endif 326 { 327 int argc = 2; 328 char *argv[3]; 329 argv[0] = "dummy"; 330 argv[1] = thisarg; 331 argv[2] = NULL; 332 err += unedit (argc, argv); 333 if (restore_cwd (&cwd)) 334 error (1, errno, "Failed to restore current directory, `%s'.", 335 cwd.name); 336 } 337 338 #ifdef CLIENT_SUPPORT 339 if (current_parsed_root->isremote) 340 { 341 send_to_server ("Argument ", 0); 342 send_to_server (thisarg, 0); 343 send_to_server ("\012", 1); 344 send_to_server ("release\012", 0); 345 } 346 else 347 #endif /* CLIENT_SUPPORT */ 348 { 349 history_write ('F', thisarg, "", thisarg, ""); /* F == Free */ 350 } 351 352 if (delete_flag) 353 { 354 /* FIXME? Shouldn't this just delete the CVS-controlled 355 files and, perhaps, the files that would normally be 356 ignored and leave everything else? */ 357 358 if (unlink_file_dir (thisarg) < 0) 359 error (0, errno, "deletion of directory %s failed", thisarg); 360 } 361 362 #ifdef CLIENT_SUPPORT 363 if (current_parsed_root->isremote) 364 { 365 /* FIXME: 366 * Is there a good reason why get_server_responses() isn't 367 * responsible for restoring its initial directory itself when 368 * finished? 369 */ 370 err += get_server_responses (); 371 372 if (restore_cwd (&cwd)) 373 error (1, errno, "Failed to restore current directory, `%s'.", 374 cwd.name); 375 } 376 #endif /* CLIENT_SUPPORT */ 377 } 378 379 if (restore_cwd (&cwd)) 380 error (1, errno, "Failed to restore current directory, `%s'.", 381 cwd.name); 382 free_cwd (&cwd); 383 384 #ifdef CLIENT_SUPPORT 385 if (current_parsed_root->isremote) 386 { 387 /* Unfortunately, client.c doesn't offer a way to close 388 the connection without waiting for responses. The extra 389 network turnaround here is quite unnecessary other than 390 that.... */ 391 send_to_server ("noop\012", 0); 392 err += get_responses_and_close (); 393 } 394 #endif /* CLIENT_SUPPORT */ 395 396 if (line != NULL) 397 free (line); 398 return err; 399 } 400