117721Speter/* 2175261Sobrien * Copyright (C) 1994-2005 The Free Software Foundation, Inc. 3175261Sobrien * 4175261Sobrien * This program is free software; you can redistribute it and/or modify 5175261Sobrien * it under the terms of the GNU General Public License as published by 6175261Sobrien * the Free Software Foundation; either version 2, or (at your option) 7175261Sobrien * any later version. 8175261Sobrien * 9175261Sobrien * This program is distributed in the hope that it will be useful, 10175261Sobrien * but WITHOUT ANY WARRANTY; without even the implied warranty of 11175261Sobrien * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12175261Sobrien * GNU General Public License for more details. 13175261Sobrien */ 14175261Sobrien 15175261Sobrien/* 1617721Speter * Release: "cancel" a checkout in the history log. 1717721Speter * 1817721Speter * - Enter a line in the history log indicating the "release". - If asked to, 1917721Speter * delete the local working directory. 2017721Speter */ 2117721Speter 2217721Speter#include "cvs.h" 2354427Speter#include "savecwd.h" 2425839Speter#include "getline.h" 2517721Speter 2617721Speterstatic const char *const release_usage[] = 2717721Speter{ 2825839Speter "Usage: %s %s [-d] directories...\n", 2917721Speter "\t-d\tDelete the given directory.\n", 3032785Speter "(Specify the --help global option for a list of other help options)\n", 3117721Speter NULL 3217721Speter}; 3317721Speter 3425839Speter#ifdef SERVER_SUPPORT 3525839Speterstatic int release_server PROTO ((int argc, char **argv)); 3617721Speter 3725839Speter/* This is the server side of cvs release. */ 3825839Speterstatic int 3925839Speterrelease_server (argc, argv) 4025839Speter int argc; 4125839Speter char **argv; 4225839Speter{ 4325839Speter int i; 4417721Speter 4525839Speter /* Note that we skip argv[0]. */ 4625839Speter for (i = 1; i < argc; ++i) 4725839Speter history_write ('F', argv[i], "", argv[i], ""); 4825839Speter return 0; 4925839Speter} 5025839Speter 5125839Speter#endif /* SERVER_SUPPORT */ 5225839Speter 5325839Speter/* There are various things to improve about this implementation: 5425839Speter 5525839Speter 1. Using run_popen to run "cvs update" could be replaced by a 5617721Speter fairly simple start_recursion/classify_file loop--a win for 5725839Speter portability, performance, and cleanliness. In particular, there is 5825839Speter no particularly good way to find the right "cvs". 5917721Speter 6025839Speter 2. The fact that "cvs update" contacts the server slows things down; 6125839Speter it undermines the case for using "cvs release" rather than "rm -rf". 6225839Speter However, for correctly printing "? foo" and correctly handling 6332785Speter CVSROOTADM_IGNORE, we currently need to contact the server. (One 6432785Speter idea for how to fix this is to stash a copy of CVSROOTADM_IGNORE in 6532785Speter the working directories; see comment at base_* in entries.c for a 6632785Speter few thoughts on that). 6717721Speter 6825839Speter 3. Would be nice to take processing things on the client side one step 6925839Speter further, and making it like edit/unedit in terms of working well if 7025839Speter disconnected from the network, and then sending a delayed 7125839Speter notification. 7217721Speter 7325839Speter 4. Having separate network turnarounds for the "Notify" request 7425839Speter which we do as part of unedit, and for the "release" itself, is slow 7525839Speter and unnecessary. */ 7617721Speter 7717721Speterint 7817721Speterrelease (argc, argv) 7917721Speter int argc; 8017721Speter char **argv; 8117721Speter{ 8217721Speter FILE *fp; 8325839Speter int i, c; 8425839Speter char *line = NULL; 8525839Speter size_t line_allocated = 0; 8625839Speter char *update_cmd; 8717721Speter char *thisarg; 8817721Speter int arg_start_idx; 8917721Speter int err = 0; 9025839Speter short delete_flag = 0; 9154427Speter struct saved_cwd cwd; 9217721Speter 9317721Speter#ifdef SERVER_SUPPORT 9425839Speter if (server_active) 9525839Speter return release_server (argc, argv); 9617721Speter#endif 9725839Speter 9825839Speter /* Everything from here on is client or local. */ 9925839Speter if (argc == -1) 10025839Speter usage (release_usage); 10126065Speter optind = 0; 10225839Speter while ((c = getopt (argc, argv, "+Qdq")) != -1) 10325839Speter { 10425839Speter switch (c) 10525839Speter { 10625839Speter case 'Q': 10725839Speter case 'q': 10825839Speter error (1, 0, 10925839Speter "-q or -Q must be specified before \"%s\"", 110128266Speter cvs_cmd_name); 11117721Speter break; 11225839Speter case 'd': 11317721Speter delete_flag++; 11417721Speter break; 11525839Speter case '?': 11625839Speter default: 11717721Speter usage (release_usage); 11817721Speter break; 11925839Speter } 12025839Speter } 12125839Speter argc -= optind; 12225839Speter argv += optind; 12317721Speter 12417721Speter /* We're going to run "cvs -n -q update" and check its output; if 12517721Speter * the output is sufficiently unalarming, then we release with no 12617721Speter * questions asked. Else we prompt, then maybe release. 12754427Speter * (Well, actually we ask no matter what. Our notion of "sufficiently 12854427Speter * unalarming" doesn't take into account "? foo.c" files, so it is 12954427Speter * up to the user to take note of them, at least currently 13054427Speter * (ignore-193 in testsuite)). 13117721Speter */ 132128266Speter /* Construct the update command. Be sure to add authentication and 133128266Speter encryption if we are using them currently, else our child process may 134128266Speter not be able to communicate with the server. */ 13525839Speter update_cmd = xmalloc (strlen (program_path) 136128266Speter + strlen (current_parsed_root->original) 137128266Speter + 1 + 3 + 3 + 16 + 1); 138128266Speter sprintf (update_cmd, "%s %s%s-n -q -d %s update", 139128266Speter program_path, 140175261Sobrien#if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT) 141128266Speter cvsauthenticate ? "-a " : "", 142128266Speter cvsencrypt ? "-x " : "", 143175261Sobrien#else 144175261Sobrien "", "", 145175261Sobrien#endif 146128266Speter current_parsed_root->original); 14717721Speter 14817721Speter#ifdef CLIENT_SUPPORT 14917721Speter /* Start the server; we'll close it after looping. */ 15081404Speter if (current_parsed_root->isremote) 15125839Speter { 15217721Speter start_server (); 15317721Speter ign_setup (); 15425839Speter } 15517721Speter#endif /* CLIENT_SUPPORT */ 15617721Speter 15754427Speter /* Remember the directory where "cvs release" was invoked because 15854427Speter all args are relative to this directory and we chdir around. 15954427Speter */ 16054427Speter if (save_cwd (&cwd)) 16154427Speter error_exit (); 16254427Speter 16325839Speter arg_start_idx = 0; 16417721Speter 16517721Speter for (i = arg_start_idx; i < argc; i++) 16617721Speter { 16725839Speter thisarg = argv[i]; 16825839Speter 16917721Speter if (isdir (thisarg)) 17017721Speter { 17125839Speter if (CVS_CHDIR (thisarg) < 0) 17225839Speter { 17325839Speter if (!really_quiet) 17425839Speter error (0, errno, "can't chdir to: %s", thisarg); 17525839Speter continue; 17625839Speter } 17725839Speter if (!isdir (CVSADM)) 17825839Speter { 17925839Speter if (!really_quiet) 18025839Speter error (0, 0, "no repository directory: %s", thisarg); 18154427Speter if (restore_cwd (&cwd, NULL)) 18254427Speter error_exit (); 18325839Speter continue; 18425839Speter } 18517721Speter } 18617721Speter else 18717721Speter { 18825839Speter if (!really_quiet) 18925839Speter error (0, 0, "no such directory: %s", thisarg); 19025839Speter continue; 19117721Speter } 19217721Speter 19317721Speter if (!really_quiet) 19417721Speter { 195175261Sobrien int line_length, status; 19632785Speter 19725839Speter /* The "release" command piggybacks on "update", which 19825839Speter does the real work of finding out if anything is not 19925839Speter up-to-date with the repository. Then "release" prompts 20025839Speter the user, telling her how many files have been 20125839Speter modified, and asking if she still wants to do the 20225839Speter release. */ 20325839Speter fp = run_popen (update_cmd, "r"); 20432785Speter if (fp == NULL) 20532785Speter error (1, 0, "cannot run command %s", update_cmd); 20632785Speter 20725839Speter c = 0; 20817721Speter 20932785Speter while ((line_length = getline (&line, &line_allocated, fp)) >= 0) 21025839Speter { 21125839Speter if (strchr ("MARCZ", *line)) 21225839Speter c++; 21366525Speter (void) fputs (line, stdout); 21425839Speter } 21532785Speter if (line_length < 0 && !feof (fp)) 21632785Speter error (0, errno, "cannot read from subprocess"); 21717721Speter 21825839Speter /* If the update exited with an error, then we just want to 21925839Speter complain and go on to the next arg. Especially, we do 22025839Speter not want to delete the local copy, since it's obviously 22125839Speter not what the user thinks it is. */ 222175261Sobrien status = pclose (fp); 223175261Sobrien if (status != 0) 22425839Speter { 225175261Sobrien error (0, 0, "unable to release `%s' (%d)", thisarg, status); 22654427Speter if (restore_cwd (&cwd, NULL)) 22754427Speter error_exit (); 22825839Speter continue; 22925839Speter } 23017721Speter 23125839Speter printf ("You have [%d] altered files in this repository.\n", 23225839Speter c); 23325839Speter printf ("Are you sure you want to release %sdirectory `%s': ", 23425839Speter delete_flag ? "(and delete) " : "", thisarg); 23525839Speter c = !yesno (); 23625839Speter if (c) /* "No" */ 23725839Speter { 23825839Speter (void) fprintf (stderr, "** `%s' aborted by user choice.\n", 239128266Speter cvs_cmd_name); 24054427Speter if (restore_cwd (&cwd, NULL)) 24154427Speter error_exit (); 24225839Speter continue; 24325839Speter } 24417721Speter } 24517721Speter 246128266Speter /* Note: client.c doesn't like to have other code 247128266Speter changing the current directory on it. So a fair amount 248128266Speter of effort is needed to make sure it doesn't get confused 249128266Speter about the directory and (for example) overwrite 250128266Speter CVS/Entries file in the wrong directory. See release-17 251128266Speter through release-23. */ 252128266Speter 253128266Speter if (restore_cwd (&cwd, NULL)) 254175261Sobrien error_exit (); 255128266Speter 25617721Speter if (1 25717721Speter#ifdef CLIENT_SUPPORT 25881404Speter && !(current_parsed_root->isremote 25917721Speter && (!supported_request ("noop") 26017721Speter || !supported_request ("Notify"))) 26117721Speter#endif 26217721Speter ) 26317721Speter { 264128266Speter int argc = 2; 26525839Speter char *argv[3]; 26625839Speter argv[0] = "dummy"; 267128266Speter argv[1] = thisarg; 268128266Speter argv[2] = NULL; 26925839Speter err += unedit (argc, argv); 270128266Speter if (restore_cwd (&cwd, NULL)) 271175261Sobrien error_exit (); 27217721Speter } 27317721Speter 27417721Speter#ifdef CLIENT_SUPPORT 27581404Speter if (current_parsed_root->isremote) 27617721Speter { 27725839Speter send_to_server ("Argument ", 0); 27825839Speter send_to_server (thisarg, 0); 27925839Speter send_to_server ("\012", 1); 28025839Speter send_to_server ("release\012", 0); 28125839Speter } 28217721Speter else 28325839Speter#endif /* CLIENT_SUPPORT */ 28417721Speter { 28525839Speter history_write ('F', thisarg, "", thisarg, ""); /* F == Free */ 28625839Speter } 28725839Speter 28854427Speter if (delete_flag) 28954427Speter { 29054427Speter /* FIXME? Shouldn't this just delete the CVS-controlled 29154427Speter files and, perhaps, the files that would normally be 29254427Speter ignored and leave everything else? */ 29354427Speter 29454427Speter if (unlink_file_dir (thisarg) < 0) 29554427Speter error (0, errno, "deletion of directory %s failed", thisarg); 29654427Speter } 29754427Speter 29817721Speter#ifdef CLIENT_SUPPORT 29981404Speter if (current_parsed_root->isremote) 300128266Speter { 301128266Speter /* FIXME: 302128266Speter * Is there a good reason why get_server_responses() isn't 303128266Speter * responsible for restoring its initial directory itself when 304128266Speter * finished? 305128266Speter */ 306128266Speter err += get_server_responses (); 307128266Speter 308128266Speter if (restore_cwd (&cwd, NULL)) 309175261Sobrien error_exit (); 310128266Speter } 31117721Speter#endif /* CLIENT_SUPPORT */ 31225839Speter } 31325839Speter 31454427Speter if (restore_cwd (&cwd, NULL)) 31554427Speter error_exit (); 31654427Speter free_cwd (&cwd); 31754427Speter 31825839Speter#ifdef CLIENT_SUPPORT 31981404Speter if (current_parsed_root->isremote) 32025839Speter { 32125839Speter /* Unfortunately, client.c doesn't offer a way to close 32225839Speter the connection without waiting for responses. The extra 32325839Speter network turnaround here is quite unnecessary other than 32425839Speter that.... */ 32525839Speter send_to_server ("noop\012", 0); 32625839Speter err += get_responses_and_close (); 32725839Speter } 32825839Speter#endif /* CLIENT_SUPPORT */ 32925839Speter 33025839Speter free (update_cmd); 33125839Speter if (line != NULL) 33225839Speter free (line); 33317721Speter return err; 33417721Speter} 335