commit.c revision 1.49
1/*	$OpenBSD: commit.c,v 1.49 2005/12/22 14:59:54 xsa Exp $	*/
2/*
3 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/types.h>
28#include <sys/queue.h>
29#include <sys/stat.h>
30
31#include <errno.h>
32#include <fcntl.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37
38#include "buf.h"
39#include "cvs.h"
40#include "log.h"
41#include "proto.h"
42
43
44static int	cvs_commit_init(struct cvs_cmd *, int, char **, int *);
45static int	cvs_commit_prepare(CVSFILE *, void *);
46static int	cvs_commit_remote(CVSFILE *, void *);
47static int	cvs_commit_local(CVSFILE *, void *);
48static int	cvs_commit_pre_exec(struct cvsroot *);
49
50struct cvs_cmd cvs_cmd_commit = {
51	CVS_OP_COMMIT, CVS_REQ_CI, "commit",
52	{ "ci",  "com" },
53	"Check files into the repository",
54	"[-flR] [-F logfile | -m msg] [-r rev] ...",
55	"F:flm:Rr:",
56	NULL,
57	CF_RECURSE | CF_IGNORE | CF_SORT,
58	cvs_commit_init,
59	cvs_commit_pre_exec,
60	cvs_commit_remote,
61	cvs_commit_local,
62	NULL,
63	NULL,
64	CVS_CMD_SENDDIR | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDARGS2
65};
66
67static char *mfile = NULL;
68static char *rev = NULL;
69static char **commit_files = NULL;
70static int commit_fcount = 0;
71static int wantedstatus = 0;
72
73static int
74cvs_commit_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg)
75{
76	int ch;
77
78	while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) {
79		switch (ch) {
80		case 'F':
81			mfile = optarg;
82			break;
83		case 'f':
84			/* XXX half-implemented */
85			cmd->file_flags &= ~CF_RECURSE;
86			break;
87		case 'l':
88			cmd->file_flags &= ~CF_RECURSE;
89			break;
90		case 'm':
91			cvs_msg = xstrdup(optarg);
92			break;
93		case 'R':
94			cmd->file_flags |= CF_RECURSE;
95			break;
96		case 'r':
97			rev = optarg;
98			break;
99		default:
100			return (CVS_EX_USAGE);
101		}
102	}
103
104	if ((cvs_msg != NULL) && (mfile != NULL)) {
105		cvs_log(LP_ERR, "the -F and -m flags are mutually exclusive");
106		return (CVS_EX_USAGE);
107	}
108
109	if (mfile != NULL)
110		cvs_msg = cvs_logmsg_open(mfile);
111
112	*arg = optind;
113
114	commit_files = (argv + optind);
115	commit_fcount = (argc - optind);
116
117	return (0);
118}
119
120int
121cvs_commit_pre_exec(struct cvsroot *root)
122{
123	CVSFILE *cfp;
124	CVSFILE *tmp;
125	int ret, i, flags = CF_RECURSE | CF_IGNORE | CF_SORT;
126	struct cvs_flist added, modified, removed, *cl[3];
127	int stattype[] = { CVS_FST_ADDED, CVS_FST_MODIFIED, CVS_FST_REMOVED };
128
129	SIMPLEQ_INIT(&added);
130	SIMPLEQ_INIT(&modified);
131	SIMPLEQ_INIT(&removed);
132
133	cl[0] = &added;
134	cl[1] = &modified;
135	cl[2] = &removed;
136
137	if ((tmp = cvs_file_loadinfo(".", CF_NOFILES, NULL, NULL, 1)) == NULL)
138		return (CVS_EX_DATA);
139
140	/*
141	 * Obtain the file lists for the logmessage.
142	 */
143	for (i = 0; i < 3; i++) {
144		wantedstatus = stattype[i];
145		if (commit_fcount != 0) {
146			ret = cvs_file_getspec(commit_files, commit_fcount,
147			    flags, cvs_commit_prepare, cl[i], NULL);
148		} else {
149			ret = cvs_file_get(".", flags, cvs_commit_prepare,
150			    cl[i], NULL);
151		}
152
153		if (ret != CVS_EX_OK) {
154			cvs_file_free(tmp);
155			return (CVS_EX_DATA);
156		}
157	}
158
159	/*
160	 * If we didn't catch any file, don't call the editor.
161	 */
162	if (SIMPLEQ_EMPTY(&added) && SIMPLEQ_EMPTY(&modified) &&
163	    SIMPLEQ_EMPTY(&removed)) {
164		cvs_file_free(tmp);
165		return (0);
166	}
167
168	/*
169	 * Fetch the log message for real, with all the files.
170	 */
171	if (cvs_msg == NULL)
172		cvs_msg = cvs_logmsg_get(tmp->cf_name, &added, &modified,
173		    &removed);
174
175	cvs_file_free(tmp);
176
177	/* free the file lists */
178	for (i = 0; i < 3; i++) {
179		while (!SIMPLEQ_EMPTY(cl[i])) {
180			cfp = SIMPLEQ_FIRST(cl[i]);
181			SIMPLEQ_REMOVE_HEAD(cl[i], cf_list);
182			cvs_file_free(cfp);
183		}
184	}
185
186	if (cvs_msg == NULL)
187		return (CVS_EX_DATA);
188
189	if (root->cr_method != CVS_METHOD_LOCAL) {
190		if (cvs_logmsg_send(root, cvs_msg) < 0)
191			return (CVS_EX_PROTO);
192
193		if (rev != NULL) {
194			if ((cvs_sendarg(root, "-r", 0) < 0) ||
195			    (cvs_sendarg(root, rev, 0) < 0))
196				return (CVS_EX_PROTO);
197		}
198	}
199
200	return (0);
201}
202
203/*
204 * cvs_commit_prepare()
205 *
206 * Examine the file <cf> to see if it will be part of the commit, in which
207 * case it gets added to the list passed as second argument.
208 */
209int
210cvs_commit_prepare(CVSFILE *cf, void *arg)
211{
212	CVSFILE *copy;
213	struct cvs_flist *clp = (struct cvs_flist *)arg;
214
215	if ((cf->cf_type == DT_REG) && (cf->cf_cvstat == wantedstatus)) {
216		copy = cvs_file_copy(cf);
217		if (copy == NULL)
218			return (CVS_EX_DATA);
219
220		SIMPLEQ_INSERT_TAIL(clp, copy, cf_list);
221	}
222
223	return (0);
224}
225
226
227/*
228 * cvs_commit_remote()
229 *
230 * Commit a single file.
231 */
232int
233cvs_commit_remote(CVSFILE *cf, void *arg)
234{
235	int ret;
236	char *repo, fpath[MAXPATHLEN];
237	RCSFILE *rf;
238	struct cvsroot *root;
239
240	ret = 0;
241	rf = NULL;
242	repo = NULL;
243	root = CVS_DIR_ROOT(cf);
244
245	if (cf->cf_type == DT_DIR) {
246		if (cf->cf_cvstat != CVS_FST_UNKNOWN) {
247			if (cvs_senddir(root, cf) < 0)
248				return (CVS_EX_PROTO);
249		}
250		return (0);
251	}
252
253	cvs_file_getpath(cf, fpath, sizeof(fpath));
254
255	if (cf->cf_parent != NULL)
256		repo = cf->cf_parent->cf_repo;
257
258	if ((cf->cf_cvstat == CVS_FST_ADDED) ||
259	    (cf->cf_cvstat == CVS_FST_MODIFIED) ||
260	    (cf->cf_cvstat == CVS_FST_REMOVED)) {
261		if (cvs_sendentry(root, cf) < 0) {
262			return (CVS_EX_PROTO);
263		}
264
265		/* if it's removed, don't bother sending a
266		 * Modified request together with the file its
267		 * contents.
268		 */
269		if (cf->cf_cvstat == CVS_FST_REMOVED)
270			return (0);
271
272		if (cvs_sendreq(root, CVS_REQ_MODIFIED, cf->cf_name) < 0)
273			return (CVS_EX_PROTO);
274
275		if (cvs_sendfile(root, fpath) < 0) {
276			return (CVS_EX_PROTO);
277		}
278	}
279
280	return (0);
281}
282
283static int
284cvs_commit_local(CVSFILE *cf, void *arg)
285{
286	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
287
288	if (cf->cf_type == DT_DIR) {
289		if (verbosity > 1)
290			cvs_log(LP_NOTICE, "Examining %s", cf->cf_name);
291		return (0);
292	}
293
294	cvs_file_getpath(cf, fpath, sizeof(fpath));
295	cvs_rcs_getpath(cf, rcspath, sizeof(rcspath));
296
297	return (0);
298}
299