xref: /openbsd-src/usr.bin/cvs/commit.c (revision 298116df5b000b61a69743d21c92035418df8900)
1 /*	$OpenBSD: commit.c,v 1.46 2005/07/27 16:42:19 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 = strdup(optarg);
92 			if (cvs_msg == NULL) {
93 				cvs_log(LP_ERRNO, "failed to copy message");
94 				return (CVS_EX_USAGE);
95 			}
96 			break;
97 		case 'R':
98 			cmd->file_flags |= CF_RECURSE;
99 			break;
100 		case 'r':
101 			rev = optarg;
102 			break;
103 		default:
104 			return (CVS_EX_USAGE);
105 		}
106 	}
107 
108 	if ((cvs_msg != NULL) && (mfile != NULL)) {
109 		cvs_log(LP_ERR, "the -F and -m flags are mutually exclusive");
110 		return (CVS_EX_USAGE);
111 	}
112 
113 	if ((mfile != NULL) && (cvs_msg = cvs_logmsg_open(mfile)) == NULL)
114 		return (CVS_EX_DATA);
115 
116 	*arg = optind;
117 
118 	commit_files = (argv + optind);
119 	commit_fcount = (argc - optind);
120 
121 	return (0);
122 }
123 
124 int
125 cvs_commit_pre_exec(struct cvsroot *root)
126 {
127 	CVSFILE *cfp;
128 	CVSFILE *tmp;
129 	int ret, i, flags = CF_RECURSE | CF_IGNORE | CF_SORT;
130 	struct cvs_flist added, modified, removed, *cl[3];
131 	int stattype[] = { CVS_FST_ADDED, CVS_FST_MODIFIED, CVS_FST_REMOVED };
132 
133 	SIMPLEQ_INIT(&added);
134 	SIMPLEQ_INIT(&modified);
135 	SIMPLEQ_INIT(&removed);
136 
137 	cl[0] = &added;
138 	cl[1] = &modified;
139 	cl[2] = &removed;
140 
141 	if ((tmp = cvs_file_loadinfo(".", CF_NOFILES, NULL, NULL, 1)) == NULL)
142 		return (CVS_EX_DATA);
143 
144 	/*
145 	 * Obtain the file lists for the logmessage.
146 	 */
147 	for (i = 0; i < 3; i++) {
148 		wantedstatus = stattype[i];
149 		if (commit_fcount != 0) {
150 			ret = cvs_file_getspec(commit_files, commit_fcount,
151 			    flags, cvs_commit_prepare, cl[i], NULL);
152 		} else {
153 			ret = cvs_file_get(".", flags, cvs_commit_prepare,
154 			    cl[i], NULL);
155 		}
156 
157 		if (ret != CVS_EX_OK) {
158 			cvs_file_free(tmp);
159 			return (CVS_EX_DATA);
160 		}
161 	}
162 
163 	/*
164 	 * If we didn't catch any file, don't call the editor.
165 	 */
166 	if (SIMPLEQ_EMPTY(&added) && SIMPLEQ_EMPTY(&modified) &&
167 	    SIMPLEQ_EMPTY(&removed)) {
168 		cvs_file_free(tmp);
169 		return (0);
170 	}
171 
172 	/*
173 	 * Fetch the log message for real, with all the files.
174 	 */
175 	if (cvs_msg == NULL)
176 		cvs_msg = cvs_logmsg_get(tmp->cf_name, &added, &modified,
177 		    &removed);
178 
179 	cvs_file_free(tmp);
180 
181 	/* free the file lists */
182 	for (i = 0; i < 3; i++) {
183 		while (!SIMPLEQ_EMPTY(cl[i])) {
184 			cfp = SIMPLEQ_FIRST(cl[i]);
185 			SIMPLEQ_REMOVE_HEAD(cl[i], cf_list);
186 			cvs_file_free(cfp);
187 		}
188 	}
189 
190 	if (cvs_msg == NULL)
191 		return (CVS_EX_DATA);
192 
193 	if (root->cr_method != CVS_METHOD_LOCAL) {
194 		if (cvs_logmsg_send(root, cvs_msg) < 0)
195 			return (CVS_EX_PROTO);
196 
197 		if (rev != NULL) {
198 			if ((cvs_sendarg(root, "-r", 0) < 0) ||
199 			    (cvs_sendarg(root, rev, 0) < 0))
200 				return (CVS_EX_PROTO);
201 		}
202 	}
203 
204 	return (0);
205 }
206 
207 /*
208  * cvs_commit_prepare()
209  *
210  * Examine the file <cf> to see if it will be part of the commit, in which
211  * case it gets added to the list passed as second argument.
212  */
213 int
214 cvs_commit_prepare(CVSFILE *cf, void *arg)
215 {
216 	CVSFILE *copy;
217 	struct cvs_flist *clp = (struct cvs_flist *)arg;
218 
219 	if ((cf->cf_type == DT_REG) && (cf->cf_cvstat == wantedstatus)) {
220 		copy = cvs_file_copy(cf);
221 		if (copy == NULL)
222 			return (CVS_EX_DATA);
223 
224 		SIMPLEQ_INSERT_TAIL(clp, copy, cf_list);
225 	}
226 
227 	return (0);
228 }
229 
230 
231 /*
232  * cvs_commit_remote()
233  *
234  * Commit a single file.
235  */
236 int
237 cvs_commit_remote(CVSFILE *cf, void *arg)
238 {
239 	int ret;
240 	char *repo, fpath[MAXPATHLEN];
241 	RCSFILE *rf;
242 	struct cvsroot *root;
243 
244 	ret = 0;
245 	rf = NULL;
246 	repo = NULL;
247 	root = CVS_DIR_ROOT(cf);
248 
249 	if (cf->cf_type == DT_DIR) {
250 		if (cf->cf_cvstat != CVS_FST_UNKNOWN) {
251 			if (cvs_senddir(root, cf) < 0)
252 				return (CVS_EX_PROTO);
253 		}
254 		return (0);
255 	}
256 
257 	cvs_file_getpath(cf, fpath, sizeof(fpath));
258 
259 	if (cf->cf_parent != NULL)
260 		repo = cf->cf_parent->cf_repo;
261 
262 	if ((cf->cf_cvstat == CVS_FST_ADDED) ||
263 	    (cf->cf_cvstat == CVS_FST_MODIFIED) ||
264 	    (cf->cf_cvstat == CVS_FST_REMOVED)) {
265 		if (cvs_sendentry(root, cf) < 0) {
266 			return (CVS_EX_PROTO);
267 		}
268 
269 		/* if it's removed, don't bother sending a
270 		 * Modified request together with the file its
271 		 * contents.
272 		 */
273 		if (cf->cf_cvstat == CVS_FST_REMOVED)
274 			return (0);
275 
276 		if (cvs_sendreq(root, CVS_REQ_MODIFIED, cf->cf_name) < 0)
277 			return (CVS_EX_PROTO);
278 
279 		if (cvs_sendfile(root, fpath) < 0) {
280 			return (CVS_EX_PROTO);
281 		}
282 	}
283 
284 	return (0);
285 }
286 
287 static int
288 cvs_commit_local(CVSFILE *cf, void *arg)
289 {
290 	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
291 
292 	if (cf->cf_type == DT_DIR) {
293 		if (verbosity > 1)
294 			cvs_log(LP_NOTICE, "Examining %s", cf->cf_name);
295 		return (0);
296 	}
297 
298 	cvs_file_getpath(cf, fpath, sizeof(fpath));
299 
300 	if (cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)) == NULL)
301 		return (CVS_EX_DATA);
302 
303 	return (0);
304 }
305