PageRenderTime 34ms CodeModel.GetById 12ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/cvs/src/release.c

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