xref: /openbsd-src/usr.bin/cvs/commit.c (revision 3aaa63eb46949490a39db9c6d82aacc8ee5d8551)
1*3aaa63ebSderaadt /*	$OpenBSD: commit.c,v 1.160 2019/06/28 13:35:00 deraadt Exp $	*/
208f90673Sjfb /*
33ad3fb45Sjoris  * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
42ec286b3Sxsa  * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org>
508f90673Sjfb  *
63ad3fb45Sjoris  * Permission to use, copy, modify, and distribute this software for any
73ad3fb45Sjoris  * purpose with or without fee is hereby granted, provided that the above
83ad3fb45Sjoris  * copyright notice and this permission notice appear in all copies.
908f90673Sjfb  *
103ad3fb45Sjoris  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113ad3fb45Sjoris  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123ad3fb45Sjoris  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133ad3fb45Sjoris  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143ad3fb45Sjoris  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153ad3fb45Sjoris  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163ad3fb45Sjoris  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1708f90673Sjfb  */
1808f90673Sjfb 
191f8531bdSotto #include <sys/stat.h>
201f8531bdSotto 
211f8531bdSotto #include <errno.h>
221f8531bdSotto #include <fcntl.h>
23397ddb8aSnicm #include <stdlib.h>
241f8531bdSotto #include <string.h>
251f8531bdSotto #include <unistd.h>
2608f90673Sjfb 
2708f90673Sjfb #include "cvs.h"
283ad3fb45Sjoris #include "diff.h"
299fac60a5Sjoris #include "remote.h"
3008f90673Sjfb 
3184920933Stobias void			 cvs_commit_local(struct cvs_file *);
3284920933Stobias void			 cvs_commit_check_files(struct cvs_file *);
33e2ec91a3Sjoris void			 cvs_commit_loginfo(char *);
3484920933Stobias void			 cvs_commit_lock_dirs(struct cvs_file *);
3584920933Stobias 
3684920933Stobias static BUF *commit_diff(struct cvs_file *, RCSNUM *, int);
3784920933Stobias static void commit_desc_set(struct cvs_file *);
3884920933Stobias 
39b034d592Sjoris struct	file_info_list	 files_info;
40b034d592Sjoris struct	trigger_list	*line_list;
41db758d52Sjoris 
42e2ec91a3Sjoris struct cvs_flisthead	files_affected;
43e2ec91a3Sjoris struct cvs_flisthead	files_added;
44e2ec91a3Sjoris struct cvs_flisthead	files_removed;
45e2ec91a3Sjoris struct cvs_flisthead	files_modified;
46e2ec91a3Sjoris 
47db758d52Sjoris char	*logmsg = NULL;
48b034d592Sjoris char	*loginfo = NULL;
4908f90673Sjfb 
5063c546bcSnicm static int	conflicts_found;
5163c546bcSnicm 
52e4276007Sjfb struct cvs_cmd cvs_cmd_commit = {
53beba978cSjoris 	CVS_OP_COMMIT, CVS_USE_WDIR, "commit",
54e4276007Sjfb 	{ "ci", "com" },
55e4276007Sjfb 	"Check files into the repository",
56e4276007Sjfb 	"[-flR] [-F logfile | -m msg] [-r rev] ...",
57e4276007Sjfb 	"F:flm:Rr:",
5816cfc147Sjoris 	NULL,
593ad3fb45Sjoris 	cvs_commit
6016cfc147Sjoris };
6108f90673Sjfb 
623ad3fb45Sjoris int
cvs_commit(int argc,char ** argv)633ad3fb45Sjoris cvs_commit(int argc, char **argv)
6408f90673Sjfb {
651890abdaSjoris 	int flags;
66ea48eba6Sjoris 	int ch, Fflag, mflag;
67ea48eba6Sjoris 	struct module_checkout *mc;
683ad3fb45Sjoris 	struct cvs_recursion cr;
69b034d592Sjoris 	struct cvs_filelist *l;
70b034d592Sjoris 	struct file_info *fi;
71b9fc9a72Sderaadt 	char *arg = ".", repo[PATH_MAX];
7208f90673Sjfb 
731890abdaSjoris 	flags = CR_RECURSE_DIRS;
74f4d74cd1Stobias 	Fflag = mflag = 0;
751890abdaSjoris 
763ad3fb45Sjoris 	while ((ch = getopt(argc, argv, cvs_cmd_commit.cmd_opts)) != -1) {
7708f90673Sjfb 		switch (ch) {
783ad3fb45Sjoris 		case 'F':
79f4d74cd1Stobias 			/* free previously assigned value */
80397ddb8aSnicm 			free(logmsg);
8116190807Sjoris 			logmsg = cvs_logmsg_read(optarg);
82f4d74cd1Stobias 			Fflag = 1;
8308f90673Sjfb 			break;
846f8f2f56Sxsa 		case 'f':
856f8f2f56Sxsa 			break;
8608f90673Sjfb 		case 'l':
871890abdaSjoris 			flags &= ~CR_RECURSE_DIRS;
8808f90673Sjfb 			break;
8908f90673Sjfb 		case 'm':
90f4d74cd1Stobias 			/* free previously assigned value */
91397ddb8aSnicm 			free(logmsg);
923ad3fb45Sjoris 			logmsg = xstrdup(optarg);
93f4d74cd1Stobias 			mflag = 1;
9408f90673Sjfb 			break;
953ad3fb45Sjoris 		case 'R':
96bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
978d550b2eSxsa 			break;
986f8f2f56Sxsa 		case 'r':
996f8f2f56Sxsa 			break;
10008f90673Sjfb 		default:
1013ad3fb45Sjoris 			fatal("%s", cvs_cmd_commit.cmd_synopsis);
10208f90673Sjfb 		}
10308f90673Sjfb 	}
10408f90673Sjfb 
1053ad3fb45Sjoris 	argc -= optind;
1063ad3fb45Sjoris 	argv += optind;
10708f90673Sjfb 
108f4d74cd1Stobias 	/* -F and -m are mutually exclusive */
109f4d74cd1Stobias 	if (Fflag && mflag)
110f4d74cd1Stobias 		fatal("cannot specify both a log file and a message");
111f4d74cd1Stobias 
112f106b389Sjoris 	RB_INIT(&files_affected);
113f106b389Sjoris 	RB_INIT(&files_added);
114f106b389Sjoris 	RB_INIT(&files_removed);
115f106b389Sjoris 	RB_INIT(&files_modified);
116e2ec91a3Sjoris 
117b034d592Sjoris 	TAILQ_INIT(&files_info);
118db758d52Sjoris 	conflicts_found = 0;
11908f90673Sjfb 
1209fac60a5Sjoris 	cr.enterdir = NULL;
1219fac60a5Sjoris 	cr.leavedir = NULL;
122db758d52Sjoris 	cr.fileproc = cvs_commit_check_files;
1239fac60a5Sjoris 	cr.flags = flags;
1249fac60a5Sjoris 
1259fac60a5Sjoris 	if (argc > 0)
1269fac60a5Sjoris 		cvs_file_run(argc, argv, &cr);
1279fac60a5Sjoris 	else
1289fac60a5Sjoris 		cvs_file_run(1, &arg, &cr);
1299fac60a5Sjoris 
130db758d52Sjoris 	if (conflicts_found != 0)
131db758d52Sjoris 		fatal("%d conflicts found, please correct these first",
132db758d52Sjoris 		    conflicts_found);
133db758d52Sjoris 
134f106b389Sjoris 	if (RB_EMPTY(&files_affected))
135a066adb3Sjoris 		return (0);
136a066adb3Sjoris 
1374dcde513Sjoris 	if (cvsroot_is_remote()) {
13813b057d9Stobias 		if (logmsg == NULL) {
13913b057d9Stobias 			logmsg = cvs_logmsg_create(NULL, &files_added,
14013b057d9Stobias 			    &files_removed, &files_modified);
141db758d52Sjoris 			if (logmsg == NULL)
142db758d52Sjoris 				fatal("This shouldnt happen, honestly!");
143e2ec91a3Sjoris 		}
14480f6ca9bSjoris 		cvs_client_connect_to_server();
145db758d52Sjoris 		cr.fileproc = cvs_client_sendfile;
146db758d52Sjoris 
147db758d52Sjoris 		if (argc > 0)
148db758d52Sjoris 			cvs_file_run(argc, argv, &cr);
149db758d52Sjoris 		else
150db758d52Sjoris 			cvs_file_run(1, &arg, &cr);
151db758d52Sjoris 
152585a1783Sxsa 		if (!(flags & CR_RECURSE_DIRS))
153585a1783Sxsa 			cvs_client_send_request("Argument -l");
154585a1783Sxsa 
155f76ce13cSjoris 		cvs_client_send_logmsg(logmsg);
1569fac60a5Sjoris 		cvs_client_send_files(argv, argc);
1579fac60a5Sjoris 		cvs_client_senddir(".");
1589fac60a5Sjoris 		cvs_client_send_request("ci");
1599fac60a5Sjoris 		cvs_client_get_responses();
160db758d52Sjoris 	} else {
161b9fc9a72Sderaadt 		cvs_get_repository_name(".", repo, PATH_MAX);
162b034d592Sjoris 
163e2ec91a3Sjoris 		line_list = cvs_trigger_getlines(CVS_PATH_COMMITINFO, repo);
164b034d592Sjoris 		if (line_list != NULL) {
165f106b389Sjoris 			RB_FOREACH(l, cvs_flisthead, &files_affected) {
166b034d592Sjoris 				fi = xcalloc(1, sizeof(*fi));
167b034d592Sjoris 				fi->file_path = xstrdup(l->file_path);
168b034d592Sjoris 				TAILQ_INSERT_TAIL(&files_info, fi,
169b034d592Sjoris 				    flist);
170b034d592Sjoris 			}
171e2ec91a3Sjoris 
172b034d592Sjoris 			if (cvs_trigger_handle(CVS_TRIGGER_COMMITINFO,
173b034d592Sjoris 			    repo, NULL, line_list, &files_info)) {
174b034d592Sjoris 				cvs_log(LP_ERR,
175b034d592Sjoris 				    "Pre-commit check failed");
176b034d592Sjoris 				cvs_trigger_freelist(line_list);
177b034d592Sjoris 				goto end;
178b034d592Sjoris 			}
179e2ec91a3Sjoris 
180b034d592Sjoris 			cvs_trigger_freelist(line_list);
181b034d592Sjoris 			cvs_trigger_freeinfo(&files_info);
182b034d592Sjoris 		}
183b034d592Sjoris 
18413b057d9Stobias 		if (cvs_server_active) {
18513b057d9Stobias 			if (logmsg == NULL)
18613b057d9Stobias 				fatal("no log message specified");
18713b057d9Stobias 		} else if (logmsg == NULL) {
18813b057d9Stobias 			logmsg = cvs_logmsg_create(NULL, &files_added,
18913b057d9Stobias 			    &files_removed, &files_modified);
19013b057d9Stobias 			if (logmsg == NULL)
19113b057d9Stobias 				fatal("This shouldnt happen, honestly!");
19213b057d9Stobias 		}
19313b057d9Stobias 
194b034d592Sjoris 		if (cvs_logmsg_verify(logmsg))
195b034d592Sjoris 			goto end;
196b034d592Sjoris 
197fb3beb6cSjoris 		cr.fileproc = cvs_commit_lock_dirs;
198e2ec91a3Sjoris 		cvs_file_walklist(&files_affected, &cr);
199fb3beb6cSjoris 
200e2ec91a3Sjoris 		line_list = cvs_trigger_getlines(CVS_PATH_LOGINFO, repo);
201b034d592Sjoris 
202bc5d89feSjoris 		cr.fileproc = cvs_commit_local;
203e2ec91a3Sjoris 		cvs_file_walklist(&files_affected, &cr);
204ea48eba6Sjoris 
205b034d592Sjoris 		if (line_list != NULL) {
206e2ec91a3Sjoris 			cvs_commit_loginfo(repo);
207b034d592Sjoris 
208b034d592Sjoris 			cvs_trigger_handle(CVS_TRIGGER_LOGINFO, repo,
209b034d592Sjoris 			    loginfo, line_list, &files_info);
210b034d592Sjoris 
211397ddb8aSnicm 			free(loginfo);
212b034d592Sjoris 			cvs_trigger_freelist(line_list);
213b034d592Sjoris 			cvs_trigger_freeinfo(&files_info);
214b034d592Sjoris 		}
215b034d592Sjoris 
216ea48eba6Sjoris 		mc = cvs_module_lookup(repo);
217ea48eba6Sjoris 		if (mc->mc_prog != NULL &&
218ea48eba6Sjoris 		    (mc->mc_flags & MODULE_RUN_ON_COMMIT))
219b034d592Sjoris 			cvs_exec(mc->mc_prog, NULL, 0);
220db758d52Sjoris 	}
22184920933Stobias 
222b034d592Sjoris end:
223b034d592Sjoris 	cvs_trigger_freeinfo(&files_info);
224397ddb8aSnicm 	free(logmsg);
22516cfc147Sjoris 	return (0);
226c710bc5aSjfb }
227c710bc5aSjfb 
2283ad3fb45Sjoris void
cvs_commit_loginfo(char * repo)229e2ec91a3Sjoris cvs_commit_loginfo(char *repo)
230b034d592Sjoris {
231b034d592Sjoris 	BUF *buf;
232b9fc9a72Sderaadt 	char pwd[PATH_MAX];
233b034d592Sjoris 	struct cvs_filelist *cf;
234b034d592Sjoris 
235b034d592Sjoris 	if (getcwd(pwd, sizeof(pwd)) == NULL)
236b034d592Sjoris 		fatal("Can't get working directory");
237b034d592Sjoris 
2387bb3ddb0Sray 	buf = buf_alloc(1024);
239b034d592Sjoris 
240b034d592Sjoris 	cvs_trigger_loginfo_header(buf, repo);
241b034d592Sjoris 
242f106b389Sjoris 	if (!RB_EMPTY(&files_added)) {
2437bb3ddb0Sray 		buf_puts(buf, "Added Files:");
244b034d592Sjoris 
245f106b389Sjoris 		RB_FOREACH(cf, cvs_flisthead, &files_added) {
2467bb3ddb0Sray 			buf_putc(buf, '\n');
2477bb3ddb0Sray 			buf_putc(buf, '\t');
2487bb3ddb0Sray 			buf_puts(buf, cf->file_path);
249b034d592Sjoris 		}
250b034d592Sjoris 
2517bb3ddb0Sray 		buf_putc(buf, '\n');
252b034d592Sjoris 	}
253b034d592Sjoris 
254f106b389Sjoris 	if (!RB_EMPTY(&files_modified)) {
2557bb3ddb0Sray 		buf_puts(buf, "Modified Files:");
256b034d592Sjoris 
257f106b389Sjoris 		RB_FOREACH(cf, cvs_flisthead, &files_modified) {
2587bb3ddb0Sray 			buf_putc(buf, '\n');
2597bb3ddb0Sray 			buf_putc(buf, '\t');
2607bb3ddb0Sray 			buf_puts(buf, cf->file_path);
261b034d592Sjoris 		}
262b034d592Sjoris 
2637bb3ddb0Sray 		buf_putc(buf, '\n');
264b034d592Sjoris 	}
265b034d592Sjoris 
266f106b389Sjoris 	if (!RB_EMPTY(&files_removed)) {
2677bb3ddb0Sray 		buf_puts(buf, "Removed Files:");
268b034d592Sjoris 
269f106b389Sjoris 		RB_FOREACH(cf, cvs_flisthead, &files_removed) {
2707bb3ddb0Sray 			buf_putc(buf, '\n');
2717bb3ddb0Sray 			buf_putc(buf, '\t');
2727bb3ddb0Sray 			buf_puts(buf, cf->file_path);
273b034d592Sjoris 		}
274b034d592Sjoris 
2757bb3ddb0Sray 		buf_putc(buf, '\n');
276b034d592Sjoris 	}
277b034d592Sjoris 
2787bb3ddb0Sray 	buf_puts(buf, "Log Message:\n");
279b034d592Sjoris 
2807bb3ddb0Sray 	buf_puts(buf, logmsg);
281b034d592Sjoris 
2827bb3ddb0Sray 	buf_putc(buf, '\n');
2837bb3ddb0Sray 	buf_putc(buf, '\0');
284b034d592Sjoris 
2857bb3ddb0Sray 	loginfo = buf_release(buf);
286b034d592Sjoris }
287b034d592Sjoris 
288b034d592Sjoris void
cvs_commit_lock_dirs(struct cvs_file * cf)289fb3beb6cSjoris cvs_commit_lock_dirs(struct cvs_file *cf)
290fb3beb6cSjoris {
291b9fc9a72Sderaadt 	char repo[PATH_MAX];
292fb3beb6cSjoris 
293fb3beb6cSjoris 	cvs_get_repository_path(cf->file_wd, repo, sizeof(repo));
294fb3beb6cSjoris 	cvs_log(LP_TRACE, "cvs_commit_lock_dirs: %s", repo);
295fb3beb6cSjoris 
296fb3beb6cSjoris 	/* locks stay in place until we are fully done and exit */
297fb3beb6cSjoris 	cvs_repository_lock(repo, 1);
298fb3beb6cSjoris }
299fb3beb6cSjoris 
300fb3beb6cSjoris void
cvs_commit_check_files(struct cvs_file * cf)301db758d52Sjoris cvs_commit_check_files(struct cvs_file *cf)
30216cfc147Sjoris {
30318cf7829Sjoris 	char *tag;
30418cf7829Sjoris 	RCSNUM *branch, *brev;
30518cf7829Sjoris 
30654de3f21Stobias 	branch = brev = NULL;
30754de3f21Stobias 
308db758d52Sjoris 	cvs_log(LP_TRACE, "cvs_commit_check_files(%s)", cf->file_path);
3095805c4b0Sjoris 
3104dcde513Sjoris 	if (cvsroot_is_remote())
3117cffd7e4Sjoris 		cvs_remote_classify_file(cf);
3127cffd7e4Sjoris 	else
31351ef6581Sjoris 		cvs_file_classify(cf, cvs_directory_tag);
3143ad3fb45Sjoris 
315c1fe09b4Sjoris 	if (cf->file_type == CVS_DIR) {
316c1fe09b4Sjoris 		if (verbosity > 1)
317c1fe09b4Sjoris 			cvs_log(LP_NOTICE, "Examining %s", cf->file_path);
318c1fe09b4Sjoris 		return;
319c1fe09b4Sjoris 	}
320c1fe09b4Sjoris 
32109d28507Stobias 	if (cf->file_status == FILE_UPTODATE)
32209d28507Stobias 		return;
32309d28507Stobias 
32409d28507Stobias 	if (cf->file_status == FILE_MERGE ||
32509d28507Stobias 	    cf->file_status == FILE_PATCH ||
32609d28507Stobias 	    cf->file_status == FILE_CHECKOUT ||
32709d28507Stobias 	    cf->file_status == FILE_LOST ||
328846e2b74Sjoris 	    cf->file_status == FILE_UNLINK) {
32909d28507Stobias 		cvs_log(LP_ERR, "conflict: %s is not up-to-date",
33009d28507Stobias 		    cf->file_path);
3313ad3fb45Sjoris 		conflicts_found++;
332846e2b74Sjoris 		return;
333846e2b74Sjoris 	}
3343ad3fb45Sjoris 
33509d28507Stobias 	if (cf->file_status == FILE_CONFLICT &&
33609d28507Stobias 	   cf->file_ent->ce_conflict != NULL) {
337f9872b43Sjoris 		cvs_log(LP_ERR, "conflict: unresolved conflicts in %s from "
338f9872b43Sjoris 		    "merging, please fix these first", cf->file_path);
339f9872b43Sjoris 		conflicts_found++;
340846e2b74Sjoris 		return;
341f9872b43Sjoris 	}
342f9872b43Sjoris 
34309d28507Stobias 	if (cf->file_status == FILE_MODIFIED &&
34409d28507Stobias 	    cf->file_ent->ce_conflict != NULL &&
34509d28507Stobias 	    update_has_conflict_markers(cf)) {
34609d28507Stobias 		cvs_log(LP_ERR, "warning: file %s seems to still contain "
34709d28507Stobias 		    "conflict indicators", cf->file_path);
3482a132f99Sjoris 	}
3492a132f99Sjoris 
35062312420Sjoris 	if (cf->file_ent != NULL && cf->file_ent->ce_date != -1) {
35162312420Sjoris 		cvs_log(LP_ERR, "conflict: cannot commit to sticky date for %s",
35262312420Sjoris 		    cf->file_path);
35362312420Sjoris 		conflicts_found++;
35462312420Sjoris 		return;
35562312420Sjoris 	}
35662312420Sjoris 
3574dcde513Sjoris 	if (cvsroot_is_local()) {
35818cf7829Sjoris 		tag = cvs_directory_tag;
35918cf7829Sjoris 		if (cf->file_ent != NULL)
36018cf7829Sjoris 			tag = cf->file_ent->ce_tag;
36118cf7829Sjoris 
36218cf7829Sjoris 		if (tag != NULL && cf->file_rcs != NULL) {
36318cf7829Sjoris 			brev = rcs_sym_getrev(cf->file_rcs, tag);
36418cf7829Sjoris 			if (brev != NULL) {
36559eae9b8Sjoris 				if (!RCSNUM_ISBRANCH(brev)) {
36609d28507Stobias 					cvs_log(LP_ERR, "sticky tag %s is not "
36709d28507Stobias 					    "a branch for file %s", tag,
36809d28507Stobias 					    cf->file_path);
36918cf7829Sjoris 					conflicts_found++;
37018cf7829Sjoris 				}
37118cf7829Sjoris 			}
37218cf7829Sjoris 		}
37318cf7829Sjoris 	}
37418cf7829Sjoris 
37553ce2177Sfcambus 	free(branch);
37653ce2177Sfcambus 	free(brev);
37754de3f21Stobias 
37884920933Stobias 	if (cf->file_status != FILE_ADDED &&
37984920933Stobias 	    cf->file_status != FILE_REMOVED &&
38084920933Stobias 	    cf->file_status != FILE_MODIFIED)
38184920933Stobias 		return;
38284920933Stobias 
3834cb553bbSjoris 	cvs_file_get(cf->file_path, 0, &files_affected, CVS_FILE);
384db758d52Sjoris 
385db758d52Sjoris 	switch (cf->file_status) {
386db758d52Sjoris 	case FILE_ADDED:
3874cb553bbSjoris 		cvs_file_get(cf->file_path, 0, &files_added, CVS_FILE);
388db758d52Sjoris 		break;
389db758d52Sjoris 	case FILE_REMOVED:
3904cb553bbSjoris 		cvs_file_get(cf->file_path, 0, &files_removed, CVS_FILE);
391db758d52Sjoris 		break;
392db758d52Sjoris 	case FILE_MODIFIED:
3934cb553bbSjoris 		cvs_file_get(cf->file_path, 0, &files_modified, CVS_FILE);
394db758d52Sjoris 		break;
395db758d52Sjoris 	}
396ef6e385cSjoris }
397ef6e385cSjoris 
3983ad3fb45Sjoris void
cvs_commit_local(struct cvs_file * cf)3993ad3fb45Sjoris cvs_commit_local(struct cvs_file *cf)
400213afef1Sjfb {
40118cf7829Sjoris 	char *tag;
4024d33f394Sjoris 	BUF *b, *d;
40345b98c07Stobias 	int onbranch, isnew, histtype, branchadded;
40418cf7829Sjoris 	RCSNUM *nrev, *crev, *rrev, *brev;
405c486465dSxsa 	int openflags, rcsflags;
4067fa064caStobias 	char rbuf[CVS_REV_BUFSZ], nbuf[CVS_REV_BUFSZ];
407d77e7b73Sjoris 	CVSENTRIES *entlist;
408b9fc9a72Sderaadt 	char attic[PATH_MAX], repo[PATH_MAX], rcsfile[PATH_MAX];
409b034d592Sjoris 	struct file_info *fi;
410213afef1Sjfb 
4113ad3fb45Sjoris 	cvs_log(LP_TRACE, "cvs_commit_local(%s)", cf->file_path);
41251ef6581Sjoris 	cvs_file_classify(cf, cvs_directory_tag);
413213afef1Sjfb 
4147938e528Sjoris 	if (cvs_noexec == 1)
4157938e528Sjoris 		return;
4167938e528Sjoris 
4177fcaca70Sjoris 	if (cf->file_type != CVS_FILE)
4187fcaca70Sjoris 		fatal("cvs_commit_local: '%s' is not a file", cf->file_path);
4197fcaca70Sjoris 
42045b98c07Stobias 	tag = cvs_directory_tag;
42145b98c07Stobias 	if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL)
42245b98c07Stobias 		tag = cf->file_ent->ce_tag;
42345b98c07Stobias 
42445b98c07Stobias 	branchadded = 0;
42545b98c07Stobias 	switch (cf->file_status) {
42645b98c07Stobias 	case FILE_ADDED:
42745b98c07Stobias 		if (cf->file_rcs == NULL && tag != NULL) {
42845b98c07Stobias 			branchadded = 1;
42945b98c07Stobias 			cvs_add_tobranch(cf, tag);
43045b98c07Stobias 		}
43145b98c07Stobias 		break;
43245b98c07Stobias 	case FILE_MODIFIED:
43345b98c07Stobias 	case FILE_REMOVED:
43445b98c07Stobias 		if (cf->file_rcs == NULL) {
43545b98c07Stobias 			cvs_log(LP_ERR, "RCS file for %s got lost",
43645b98c07Stobias 			    cf->file_path);
43745b98c07Stobias 			return;
43845b98c07Stobias 		}
43945b98c07Stobias 		break;
44045b98c07Stobias 	default:
4413901dfa5Sjoris 		cvs_log(LP_ERR, "skipping bogus file `%s'", cf->file_path);
4423901dfa5Sjoris 		return;
4433901dfa5Sjoris 	}
4443901dfa5Sjoris 
44518cf7829Sjoris 	onbranch = 0;
44618cf7829Sjoris 	nrev = RCS_HEAD_REV;
44718cf7829Sjoris 	crev = NULL;
44818cf7829Sjoris 	rrev = NULL;
4494cd27328Sjoris 	d = NULL;
45018cf7829Sjoris 
451eb6d38a6Sjoris 	if (cf->file_rcs != NULL && cf->file_rcs->rf_branch != NULL) {
45253ce2177Sfcambus 		free(cf->file_rcs->rf_branch);
453b6bee913Sjoris 		cf->file_rcs->rf_branch = NULL;
454b6bee913Sjoris 	}
455b6bee913Sjoris 
45645b98c07Stobias 	if (cf->file_rcs != NULL) {
45718cf7829Sjoris 		rrev = rcs_head_get(cf->file_rcs);
45818cf7829Sjoris 		crev = rcs_head_get(cf->file_rcs);
4599af0ab72Stobias 		if (crev == NULL || rrev == NULL)
460e28eda4eStobias 			fatal("no head revision in RCS file for %s",
461e28eda4eStobias 			    cf->file_path);
46218cf7829Sjoris 
46318cf7829Sjoris 		if (tag != NULL) {
46453ce2177Sfcambus 			free(crev);
46553ce2177Sfcambus 			free(rrev);
466e1936db1Stobias 			brev = rcs_sym_getrev(cf->file_rcs, tag);
46718cf7829Sjoris 			crev = rcs_translate_tag(tag, cf->file_rcs);
468e1936db1Stobias 			if (brev == NULL || crev == NULL) {
46918cf7829Sjoris 				fatal("failed to resolve existing tag: %s",
47018cf7829Sjoris 				    tag);
47118cf7829Sjoris 			}
47218cf7829Sjoris 
473e1936db1Stobias 			rrev = rcsnum_alloc();
474e1936db1Stobias 			rcsnum_cpy(brev, rrev, brev->rn_len - 1);
475e1936db1Stobias 
476e1936db1Stobias 			if (RCSNUM_ISBRANCHREV(crev) &&
477e1936db1Stobias 			    rcsnum_cmp(crev, rrev, 0)) {
47818cf7829Sjoris 				nrev = rcsnum_alloc();
47918cf7829Sjoris 				rcsnum_cpy(crev, nrev, 0);
48018cf7829Sjoris 				rcsnum_inc(nrev);
48118cf7829Sjoris 			} else if (!RCSNUM_ISBRANCH(crev)) {
48218cf7829Sjoris 				nrev = rcsnum_brtorev(brev);
48318cf7829Sjoris 				if (nrev == NULL)
48418cf7829Sjoris 					fatal("failed to create branch rev");
48518cf7829Sjoris 			} else {
48618cf7829Sjoris 				fatal("this isnt suppose to happen, honestly");
48718cf7829Sjoris 			}
48818cf7829Sjoris 
48953ce2177Sfcambus 			free(brev);
49053ce2177Sfcambus 			free(rrev);
49118cf7829Sjoris 			rrev = rcsnum_branch_root(nrev);
49218cf7829Sjoris 
49318cf7829Sjoris 			/* branch stuff was checked in cvs_commit_check_files */
49418cf7829Sjoris 			onbranch = 1;
49518cf7829Sjoris 		}
49618cf7829Sjoris 
49718cf7829Sjoris 		rcsnum_tostr(crev, rbuf, sizeof(rbuf));
498570941ffSjoris 	} else {
4996bbad201Sjoris 		strlcpy(rbuf, "Non-existent", sizeof(rbuf));
500570941ffSjoris 	}
5016bbad201Sjoris 
50253ce2177Sfcambus 	free(rrev);
50343c5228fSjoris 	isnew = 0;
50461e51155Sjoris 	if (cf->file_status == FILE_ADDED) {
50543c5228fSjoris 		isnew = 1;
50661e51155Sjoris 		rcsflags = RCS_CREATE;
50745b98c07Stobias 		openflags = O_CREAT | O_RDONLY;
50861e51155Sjoris 		if (cf->file_rcs != NULL) {
50945b98c07Stobias 			if (!onbranch) {
510ca2dc546Sniallo 				if (cf->in_attic == 0)
51161e51155Sjoris 					cvs_log(LP_ERR, "warning: expected %s "
51245b98c07Stobias 					    "to be in the Attic",
51345b98c07Stobias 					    cf->file_path);
51461e51155Sjoris 
51561e51155Sjoris 				if (cf->file_rcs->rf_dead == 0)
51661e51155Sjoris 					cvs_log(LP_ERR, "warning: expected %s "
51761e51155Sjoris 					    "to be dead", cf->file_path);
51861e51155Sjoris 
51945b98c07Stobias 				cvs_get_repository_path(cf->file_wd, repo,
520b9fc9a72Sderaadt 				    PATH_MAX);
521b9fc9a72Sderaadt 				(void)xsnprintf(rcsfile, PATH_MAX, "%s/%s%s",
52261e51155Sjoris 				    repo, cf->file_name, RCS_FILE_EXT);
52361e51155Sjoris 
52461e51155Sjoris 				if (rename(cf->file_rpath, rcsfile) == -1)
52545b98c07Stobias 					fatal("cvs_commit_local: failed to "
52645b98c07Stobias 					    "move %s outside the Attic: %s",
52745b98c07Stobias 					    cf->file_path, strerror(errno));
52861e51155Sjoris 
529397ddb8aSnicm 				free(cf->file_rpath);
53061e51155Sjoris 				cf->file_rpath = xstrdup(rcsfile);
53145b98c07Stobias 				isnew = 0;
53245b98c07Stobias 			}
53361e51155Sjoris 
53461e51155Sjoris 			rcsflags = RCS_READ | RCS_PARSE_FULLY;
53561e51155Sjoris 			openflags = O_RDONLY;
53661e51155Sjoris 			rcs_close(cf->file_rcs);
53761e51155Sjoris 		}
53861e51155Sjoris 
53961e51155Sjoris 		cf->repo_fd = open(cf->file_rpath, openflags);
540*3aaa63ebSderaadt 		if (cf->repo_fd == -1)
5416bbad201Sjoris 			fatal("cvs_commit_local: %s", strerror(errno));
5426bbad201Sjoris 
5436bbad201Sjoris 		cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd,
544998f3c00Sotto 		    rcsflags, 0444);
5456bbad201Sjoris 		if (cf->file_rcs == NULL)
5466bbad201Sjoris 			fatal("cvs_commit_local: failed to create RCS file "
5476bbad201Sjoris 			    "for %s", cf->file_path);
5482ec286b3Sxsa 
5492ec286b3Sxsa 		commit_desc_set(cf);
55045b98c07Stobias 
55145b98c07Stobias 		if (branchadded)
55245b98c07Stobias 			strlcpy(rbuf, "Non-existent", sizeof(rbuf));
5536bbad201Sjoris 	}
5543ad3fb45Sjoris 
5552868ee63Sreyk 	if (verbosity > 1) {
5563ad3fb45Sjoris 		cvs_printf("Checking in %s:\n", cf->file_path);
5573ad3fb45Sjoris 		cvs_printf("%s <- %s\n", cf->file_rpath, cf->file_path);
5583ad3fb45Sjoris 		cvs_printf("old revision: %s; ", rbuf);
5592868ee63Sreyk 	}
5603ad3fb45Sjoris 
561e28eda4eStobias 	if (isnew == 0 && cf->file_rcs->rf_head == NULL)
562e28eda4eStobias 		fatal("no head revision in RCS file for %s", cf->file_path);
563e28eda4eStobias 
56418cf7829Sjoris 	if (isnew == 0 && onbranch == 0)
56518cf7829Sjoris 		d = commit_diff(cf, cf->file_rcs->rf_head, 0);
5663ad3fb45Sjoris 
567d77e7b73Sjoris 	if (cf->file_status == FILE_REMOVED) {
56818cf7829Sjoris 		b = rcs_rev_getbuf(cf->file_rcs, crev, 0);
56918cf7829Sjoris 	} else if (onbranch == 1) {
57018cf7829Sjoris 		b = commit_diff(cf, crev, 1);
5716bbad201Sjoris 	} else {
5727bb3ddb0Sray 		b = buf_load_fd(cf->fd);
573d77e7b73Sjoris 	}
5743ad3fb45Sjoris 
57518cf7829Sjoris 	if (isnew == 0 && onbranch == 0) {
57618cf7829Sjoris 		if (rcs_deltatext_set(cf->file_rcs, crev, d) == -1)
5773ad3fb45Sjoris 			fatal("cvs_commit_local: failed to set delta");
5786bbad201Sjoris 	}
5793ad3fb45Sjoris 
58018cf7829Sjoris 	if (rcs_rev_add(cf->file_rcs, nrev, logmsg, -1, NULL) == -1)
5813ad3fb45Sjoris 		fatal("cvs_commit_local: failed to add new revision");
5823ad3fb45Sjoris 
58318cf7829Sjoris 	if (nrev == RCS_HEAD_REV)
58418cf7829Sjoris 		nrev = cf->file_rcs->rf_head;
58518cf7829Sjoris 
58618cf7829Sjoris 	if (rcs_deltatext_set(cf->file_rcs, nrev, b) == -1)
5873ad3fb45Sjoris 		fatal("cvs_commit_local: failed to set new HEAD delta");
5883ad3fb45Sjoris 
589d77e7b73Sjoris 	if (cf->file_status == FILE_REMOVED) {
59018cf7829Sjoris 		if (rcs_state_set(cf->file_rcs, nrev, RCS_STATE_DEAD) == -1)
591d77e7b73Sjoris 			fatal("cvs_commit_local: failed to set state");
592d77e7b73Sjoris 	}
593d77e7b73Sjoris 
59456f996a2Sxsa 	if (cf->file_status == FILE_ADDED && cf->file_ent->ce_opts != NULL) {
59537fdff3fStobias 		int cf_kflag;
59656f996a2Sxsa 
59737fdff3fStobias 		cf_kflag = rcs_kflag_get(cf->file_ent->ce_opts + 2);
59837fdff3fStobias 		rcs_kwexp_set(cf->file_rcs, cf_kflag);
59956f996a2Sxsa 	}
60056f996a2Sxsa 
6013ad3fb45Sjoris 	rcs_write(cf->file_rcs);
6023ad3fb45Sjoris 
603d77e7b73Sjoris 	if (cf->file_status == FILE_REMOVED) {
6042868ee63Sreyk 		strlcpy(nbuf, "Removed", sizeof(nbuf));
6056bbad201Sjoris 	} else if (cf->file_status == FILE_ADDED) {
60661e51155Sjoris 		if (cf->file_rcs->rf_dead == 1)
6072868ee63Sreyk 			strlcpy(nbuf, "Initial Revision", sizeof(nbuf));
60819615a73Sjoris 		else
60918cf7829Sjoris 			rcsnum_tostr(nrev, nbuf, sizeof(nbuf));
610d77e7b73Sjoris 	} else if (cf->file_status == FILE_MODIFIED) {
61118cf7829Sjoris 		rcsnum_tostr(nrev, nbuf, sizeof(nbuf));
612d77e7b73Sjoris 	}
613d77e7b73Sjoris 
6142868ee63Sreyk 	if (verbosity > 1)
6152868ee63Sreyk 		cvs_printf("new revision: %s\n", nbuf);
6163ad3fb45Sjoris 
6173ad3fb45Sjoris 	(void)unlink(cf->file_path);
6183ad3fb45Sjoris 	(void)close(cf->fd);
6193ad3fb45Sjoris 	cf->fd = -1;
620d77e7b73Sjoris 
621d77e7b73Sjoris 	if (cf->file_status != FILE_REMOVED) {
62218cf7829Sjoris 		cvs_checkout_file(cf, nrev, NULL, CO_COMMIT);
623d77e7b73Sjoris 	} else {
624d77e7b73Sjoris 		entlist = cvs_ent_open(cf->file_wd);
625d77e7b73Sjoris 		cvs_ent_remove(entlist, cf->file_name);
6267fcaca70Sjoris 
627b9fc9a72Sderaadt 		cvs_get_repository_path(cf->file_wd, repo, PATH_MAX);
6287fcaca70Sjoris 
629b9fc9a72Sderaadt 		(void)xsnprintf(attic, PATH_MAX, "%s/%s",
630c486465dSxsa 		    repo, CVS_PATH_ATTIC);
6317fcaca70Sjoris 
6327fcaca70Sjoris 		if (mkdir(attic, 0755) == -1 && errno != EEXIST)
6337fcaca70Sjoris 			fatal("cvs_commit_local: failed to create Attic");
6347fcaca70Sjoris 
635b9fc9a72Sderaadt 		(void)xsnprintf(attic, PATH_MAX, "%s/%s/%s%s", repo,
6367fcaca70Sjoris 		    CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT);
6377fcaca70Sjoris 
6387fcaca70Sjoris 		if (rename(cf->file_rpath, attic) == -1)
6397fcaca70Sjoris 			fatal("cvs_commit_local: failed to move %s to Attic",
6407fcaca70Sjoris 			    cf->file_path);
6417fcaca70Sjoris 
642408908afSjoris 		if (cvs_server_active == 1)
643408908afSjoris 			cvs_server_update_entry("Remove-entry", cf);
644d77e7b73Sjoris 	}
6453ad3fb45Sjoris 
6462868ee63Sreyk 	if (verbosity > 1)
6473ad3fb45Sjoris 		cvs_printf("done\n");
6482868ee63Sreyk 	else {
6492868ee63Sreyk 		cvs_log(LP_NOTICE, "checking in '%s'; revision %s -> %s",
6502868ee63Sreyk 		    cf->file_path, rbuf, nbuf);
6512868ee63Sreyk 	}
6523901dfa5Sjoris 
653b034d592Sjoris 	if (line_list != NULL) {
654b034d592Sjoris 		fi = xcalloc(1, sizeof(*fi));
655b034d592Sjoris 		fi->file_path = xstrdup(cf->file_path);
656b034d592Sjoris 		fi->crevstr = xstrdup(rbuf);
657b034d592Sjoris 		fi->nrevstr = xstrdup(nbuf);
658241113ffSjoris 		if (tag != NULL)
659241113ffSjoris 			fi->tag_new = xstrdup(tag);
660b034d592Sjoris 		TAILQ_INSERT_TAIL(&files_info, fi, flist);
661b034d592Sjoris 	}
662b034d592Sjoris 
6633901dfa5Sjoris 	switch (cf->file_status) {
6643901dfa5Sjoris 	case FILE_MODIFIED:
6653901dfa5Sjoris 		histtype = CVS_HISTORY_COMMIT_MODIFIED;
6663901dfa5Sjoris 		break;
6673901dfa5Sjoris 	case FILE_ADDED:
6683901dfa5Sjoris 		histtype = CVS_HISTORY_COMMIT_ADDED;
6693901dfa5Sjoris 		break;
6703901dfa5Sjoris 	case FILE_REMOVED:
6713901dfa5Sjoris 		histtype = CVS_HISTORY_COMMIT_REMOVED;
6723901dfa5Sjoris 		break;
6734cd27328Sjoris 	default:
6744cd27328Sjoris 		histtype = -1;
6754cd27328Sjoris 		break;
6763901dfa5Sjoris 	}
6773901dfa5Sjoris 
67853ce2177Sfcambus 	free(crev);
6794649b416Sjoris 
6804cd27328Sjoris 	if (histtype != -1)
6813901dfa5Sjoris 		cvs_history_add(histtype, cf, NULL);
6824cd27328Sjoris 	else
6834cd27328Sjoris 		cvs_log(LP_NOTICE, "histtype was -1 for %s", cf->file_path);
684213afef1Sjfb }
685c710bc5aSjfb 
6864d33f394Sjoris static BUF *
commit_diff(struct cvs_file * cf,RCSNUM * rev,int reverse)68718cf7829Sjoris commit_diff(struct cvs_file *cf, RCSNUM *rev, int reverse)
68808f90673Sjfb {
6897945e3ffSnicm 	int fd1, fd2, d;
6907945e3ffSnicm 	char *p1, *p2;
691b794e5f5Stobias 	BUF *b;
692924e91c6Sjoris 
693924e91c6Sjoris 	(void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
69408f90673Sjfb 
69519615a73Sjoris 	if (cf->file_status == FILE_MODIFIED ||
69619615a73Sjoris 	    cf->file_status == FILE_ADDED) {
6977bb3ddb0Sray 		b = buf_load_fd(cf->fd);
6987bb3ddb0Sray 		fd1 = buf_write_stmp(b, p1, NULL);
6997bb3ddb0Sray 		buf_free(b);
700924e91c6Sjoris 	} else {
7012e0d696aSjoris 		fd1 = rcs_rev_write_stmp(cf->file_rcs, rev, p1, 0);
702924e91c6Sjoris 	}
7036169aed9Skrapht 
7043ad3fb45Sjoris 	(void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
7052e0d696aSjoris 	fd2 = rcs_rev_write_stmp(cf->file_rcs, rev, p2, RCS_KWEXP_NONE);
706924e91c6Sjoris 
7077bb3ddb0Sray 	b = buf_alloc(128);
70838c61fc2Sjoris 
7093ad3fb45Sjoris 	diff_format = D_RCSDIFF;
71018cf7829Sjoris 
7117945e3ffSnicm 	if (reverse == 1)
7127945e3ffSnicm 		d = diffreg(p2, p1, fd2, fd1, b, D_FORCEASCII);
7137945e3ffSnicm 	else
7147945e3ffSnicm 		d = diffreg(p1, p2, fd1, fd2, b, D_FORCEASCII);
7157945e3ffSnicm 	if (d == D_ERROR)
71618cf7829Sjoris 		fatal("commit_diff: failed to get RCS patch");
717740595fcSxsa 
7182e0d696aSjoris 	close(fd1);
7192e0d696aSjoris 	close(fd2);
7202e0d696aSjoris 
721397ddb8aSnicm 	free(p1);
722397ddb8aSnicm 	free(p2);
7238787a230Sjoris 
724b794e5f5Stobias 	return (b);
7256169aed9Skrapht }
7262ec286b3Sxsa 
7272ec286b3Sxsa static void
commit_desc_set(struct cvs_file * cf)7282ec286b3Sxsa commit_desc_set(struct cvs_file *cf)
7292ec286b3Sxsa {
7302ec286b3Sxsa 	BUF *bp;
731c486465dSxsa 	int fd;
732b9fc9a72Sderaadt 	char desc_path[PATH_MAX], *desc;
7332ec286b3Sxsa 
734b9fc9a72Sderaadt 	(void)xsnprintf(desc_path, PATH_MAX, "%s/%s/%s%s",
735c4d292c1Stobias 	    cf->file_wd, CVS_PATH_CVSDIR, cf->file_name, CVS_DESCR_FILE_EXT);
7362ec286b3Sxsa 
737ba7b4b60Sotto 	if ((fd = open(desc_path, O_RDONLY)) == -1)
7382ec286b3Sxsa 		return;
7392ec286b3Sxsa 
7407bb3ddb0Sray 	bp = buf_load_fd(fd);
7417bb3ddb0Sray 	buf_putc(bp, '\0');
7427bb3ddb0Sray 	desc = buf_release(bp);
7432ec286b3Sxsa 
7442ec286b3Sxsa 	rcs_desc_set(cf->file_rcs, desc);
7452ec286b3Sxsa 
7462d02866cSjoris 	(void)close(fd);
7472ec286b3Sxsa 	(void)cvs_unlink(desc_path);
7482ec286b3Sxsa 
749397ddb8aSnicm 	free(desc);
7502ec286b3Sxsa }
751