xref: /openbsd-src/usr.bin/cvs/commit.c (revision ca321df7f0a15bc97a5e273de879e60649b8a07c)
1 /*	$OpenBSD: commit.c,v 1.52 2006/03/16 09:06: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 "includes.h"
28 
29 #include "buf.h"
30 #include "cvs.h"
31 #include "log.h"
32 #include "proto.h"
33 
34 
35 static int	cvs_commit_init(struct cvs_cmd *, int, char **, int *);
36 static int	cvs_commit_prepare(CVSFILE *, void *);
37 static int	cvs_commit_remote(CVSFILE *, void *);
38 static int	cvs_commit_local(CVSFILE *, void *);
39 static int	cvs_commit_pre_exec(struct cvsroot *);
40 
41 struct cvs_cmd cvs_cmd_commit = {
42 	CVS_OP_COMMIT, CVS_REQ_CI, "commit",
43 	{ "ci",  "com" },
44 	"Check files into the repository",
45 	"[-flR] [-F logfile | -m msg] [-r rev] ...",
46 	"F:flm:Rr:",
47 	NULL,
48 	CF_RECURSE | CF_IGNORE | CF_SORT,
49 	cvs_commit_init,
50 	cvs_commit_pre_exec,
51 	cvs_commit_remote,
52 	cvs_commit_local,
53 	NULL,
54 	NULL,
55 	CVS_CMD_SENDDIR | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDARGS2
56 };
57 
58 static char *mfile = NULL;
59 static char *rev = NULL;
60 static char **commit_files = NULL;
61 static int commit_fcount = 0;
62 static int wantedstatus = 0;
63 
64 static int
65 cvs_commit_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg)
66 {
67 	int ch;
68 
69 	while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) {
70 		switch (ch) {
71 		case 'F':
72 			mfile = optarg;
73 			break;
74 		case 'f':
75 			/* XXX half-implemented */
76 			cmd->file_flags &= ~CF_RECURSE;
77 			break;
78 		case 'l':
79 			cmd->file_flags &= ~CF_RECURSE;
80 			break;
81 		case 'm':
82 			cvs_msg = xstrdup(optarg);
83 			break;
84 		case 'R':
85 			cmd->file_flags |= CF_RECURSE;
86 			break;
87 		case 'r':
88 			rev = optarg;
89 			break;
90 		default:
91 			return (CVS_EX_USAGE);
92 		}
93 	}
94 
95 	if ((cvs_msg != NULL) && (mfile != NULL)) {
96 		cvs_log(LP_ERR, "the -F and -m flags are mutually exclusive");
97 		return (CVS_EX_USAGE);
98 	}
99 
100 	if (mfile != NULL)
101 		cvs_msg = cvs_logmsg_open(mfile);
102 
103 	*arg = optind;
104 
105 	commit_files = (argv + optind);
106 	commit_fcount = (argc - optind);
107 
108 	return (0);
109 }
110 
111 int
112 cvs_commit_pre_exec(struct cvsroot *root)
113 {
114 	CVSFILE *cfp;
115 	CVSFILE *tmp;
116 	int ret, i, flags = CF_RECURSE | CF_IGNORE | CF_SORT;
117 	struct cvs_flist added, modified, removed, *cl[3];
118 	int stattype[] = { CVS_FST_ADDED, CVS_FST_MODIFIED, CVS_FST_REMOVED };
119 
120 	SIMPLEQ_INIT(&added);
121 	SIMPLEQ_INIT(&modified);
122 	SIMPLEQ_INIT(&removed);
123 
124 	cl[0] = &added;
125 	cl[1] = &modified;
126 	cl[2] = &removed;
127 
128 	if ((tmp = cvs_file_loadinfo(".", CF_NOFILES, NULL, NULL, 1)) == NULL)
129 		return (CVS_EX_DATA);
130 
131 	/*
132 	 * Obtain the file lists for the logmessage.
133 	 */
134 	for (i = 0; i < 3; i++) {
135 		wantedstatus = stattype[i];
136 		if (commit_fcount != 0) {
137 			ret = cvs_file_getspec(commit_files, commit_fcount,
138 			    flags, cvs_commit_prepare, cl[i], NULL);
139 		} else {
140 			ret = cvs_file_get(".", flags, cvs_commit_prepare,
141 			    cl[i], NULL);
142 		}
143 
144 		if (ret != CVS_EX_OK) {
145 			cvs_file_free(tmp);
146 			return (CVS_EX_DATA);
147 		}
148 	}
149 
150 	/*
151 	 * If we didn't catch any file, don't call the editor.
152 	 */
153 	if (SIMPLEQ_EMPTY(&added) && SIMPLEQ_EMPTY(&modified) &&
154 	    SIMPLEQ_EMPTY(&removed)) {
155 		cvs_file_free(tmp);
156 		return (0);
157 	}
158 
159 	/*
160 	 * Fetch the log message for real, with all the files.
161 	 */
162 	if (cvs_msg == NULL)
163 		cvs_msg = cvs_logmsg_get(tmp->cf_name, &added, &modified,
164 		    &removed);
165 
166 	cvs_file_free(tmp);
167 
168 	/* free the file lists */
169 	for (i = 0; i < 3; i++) {
170 		while (!SIMPLEQ_EMPTY(cl[i])) {
171 			cfp = SIMPLEQ_FIRST(cl[i]);
172 			SIMPLEQ_REMOVE_HEAD(cl[i], cf_list);
173 			cvs_file_free(cfp);
174 		}
175 	}
176 
177 	if (cvs_msg == NULL)
178 		return (CVS_EX_DATA);
179 
180 	if (root->cr_method != CVS_METHOD_LOCAL) {
181 		cvs_logmsg_send(root, cvs_msg);
182 
183 		if (rev != NULL) {
184 			cvs_sendarg(root, "-r", 0);
185 			cvs_sendarg(root, rev, 0);
186 		}
187 	}
188 
189 	return (0);
190 }
191 
192 /*
193  * cvs_commit_prepare()
194  *
195  * Examine the file <cf> to see if it will be part of the commit, in which
196  * case it gets added to the list passed as second argument.
197  */
198 int
199 cvs_commit_prepare(CVSFILE *cf, void *arg)
200 {
201 	CVSFILE *copy;
202 	struct cvs_flist *clp = (struct cvs_flist *)arg;
203 
204 	if ((cf->cf_type == DT_REG) && (cf->cf_cvstat == wantedstatus)) {
205 		copy = cvs_file_copy(cf);
206 		if (copy == NULL)
207 			return (CVS_EX_DATA);
208 
209 		SIMPLEQ_INSERT_TAIL(clp, copy, cf_list);
210 	}
211 
212 	return (0);
213 }
214 
215 
216 /*
217  * cvs_commit_remote()
218  *
219  * Commit a single file.
220  */
221 int
222 cvs_commit_remote(CVSFILE *cf, void *arg)
223 {
224 	char *repo, fpath[MAXPATHLEN];
225 	struct cvsroot *root;
226 
227 	repo = NULL;
228 	root = CVS_DIR_ROOT(cf);
229 
230 	if (cf->cf_type == DT_DIR) {
231 		if (cf->cf_cvstat != CVS_FST_UNKNOWN)
232 			cvs_senddir(root, cf);
233 		return (0);
234 	}
235 
236 	cvs_file_getpath(cf, fpath, sizeof(fpath));
237 
238 	if (cf->cf_parent != NULL)
239 		repo = cf->cf_parent->cf_repo;
240 
241 	if ((cf->cf_cvstat == CVS_FST_ADDED) ||
242 	    (cf->cf_cvstat == CVS_FST_MODIFIED) ||
243 	    (cf->cf_cvstat == CVS_FST_REMOVED)) {
244 		cvs_sendentry(root, cf);
245 
246 		/* if it's removed, don't bother sending a
247 		 * Modified request together with the file its
248 		 * contents.
249 		 */
250 		if (cf->cf_cvstat == CVS_FST_REMOVED)
251 			return (0);
252 
253 		cvs_sendreq(root, CVS_REQ_MODIFIED, cf->cf_name);
254 		cvs_sendfile(root, fpath);
255 	}
256 
257 	return (0);
258 }
259 
260 static int
261 cvs_commit_local(CVSFILE *cf, void *arg)
262 {
263 	char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
264 
265 	if (cf->cf_type == DT_DIR) {
266 		if (verbosity > 1)
267 			cvs_log(LP_NOTICE, "Examining %s", cf->cf_name);
268 		return (0);
269 	}
270 
271 	cvs_file_getpath(cf, fpath, sizeof(fpath));
272 	cvs_rcs_getpath(cf, rcspath, sizeof(rcspath));
273 
274 	return (0);
275 }
276