xref: /openbsd-src/usr.bin/cvs/commit.c (revision 31f670fb0d7a84240bbe365cb0f25fe14b2d2897)
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 
44 static int	cvs_commit_init(struct cvs_cmd *, int, char **, int *);
45 static int	cvs_commit_prepare(CVSFILE *, void *);
46 static int	cvs_commit_remote(CVSFILE *, void *);
47 static int	cvs_commit_local(CVSFILE *, void *);
48 static int	cvs_commit_pre_exec(struct cvsroot *);
49 
50 struct 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 
67 static char *mfile = NULL;
68 static char *rev = NULL;
69 static char **commit_files = NULL;
70 static int commit_fcount = 0;
71 static int wantedstatus = 0;
72 
73 static int
74 cvs_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 
120 int
121 cvs_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  */
209 int
210 cvs_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  */
232 int
233 cvs_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 
283 static int
284 cvs_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