xref: /openbsd-src/usr.bin/cvs/commit.c (revision 37fdff3fa60fdd1baf6bc6082eee5bd7ac3b858c)
1*37fdff3fStobias /*	$OpenBSD: commit.c,v 1.126 2008/02/04 15:07:33 tobias 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>
231f8531bdSotto #include <string.h>
241f8531bdSotto #include <unistd.h>
2508f90673Sjfb 
2608f90673Sjfb #include "cvs.h"
273ad3fb45Sjoris #include "diff.h"
289fac60a5Sjoris #include "remote.h"
2908f90673Sjfb 
303ad3fb45Sjoris void	cvs_commit_local(struct cvs_file *);
31db758d52Sjoris void	cvs_commit_check_files(struct cvs_file *);
3208f90673Sjfb 
3318cf7829Sjoris static BUF *commit_diff(struct cvs_file *, RCSNUM *, int);
342ec286b3Sxsa static void commit_desc_set(struct cvs_file *);
353ad3fb45Sjoris 
363ad3fb45Sjoris struct	cvs_flisthead files_affected;
37db758d52Sjoris struct	cvs_flisthead files_added;
38db758d52Sjoris struct	cvs_flisthead files_removed;
39db758d52Sjoris struct	cvs_flisthead files_modified;
40db758d52Sjoris 
413ad3fb45Sjoris int	conflicts_found;
42db758d52Sjoris char	*logmsg = NULL;
4308f90673Sjfb 
44e4276007Sjfb struct cvs_cmd cvs_cmd_commit = {
45f331ff59Stobias 	CVS_OP_COMMIT, CVS_USE_WDIR, "commit",
46e4276007Sjfb 	{ "ci", "com" },
47e4276007Sjfb 	"Check files into the repository",
48e4276007Sjfb 	"[-flR] [-F logfile | -m msg] [-r rev] ...",
49e4276007Sjfb 	"F:flm:Rr:",
5016cfc147Sjoris 	NULL,
513ad3fb45Sjoris 	cvs_commit
5216cfc147Sjoris };
5308f90673Sjfb 
543ad3fb45Sjoris int
553ad3fb45Sjoris cvs_commit(int argc, char **argv)
5608f90673Sjfb {
57f4d74cd1Stobias 	int ch, Fflag, mflag;
583ad3fb45Sjoris 	char *arg = ".";
591890abdaSjoris 	int flags;
603ad3fb45Sjoris 	struct cvs_recursion cr;
6108f90673Sjfb 
621890abdaSjoris 	flags = CR_RECURSE_DIRS;
63f4d74cd1Stobias 	Fflag = mflag = 0;
641890abdaSjoris 
653ad3fb45Sjoris 	while ((ch = getopt(argc, argv, cvs_cmd_commit.cmd_opts)) != -1) {
6608f90673Sjfb 		switch (ch) {
673ad3fb45Sjoris 		case 'F':
68f4d74cd1Stobias 			/* free previously assigned value */
69f4d74cd1Stobias 			if (logmsg != NULL)
70f4d74cd1Stobias 				xfree(logmsg);
7116190807Sjoris 			logmsg = cvs_logmsg_read(optarg);
72f4d74cd1Stobias 			Fflag = 1;
7308f90673Sjfb 			break;
746f8f2f56Sxsa 		case 'f':
756f8f2f56Sxsa 			break;
7608f90673Sjfb 		case 'l':
771890abdaSjoris 			flags &= ~CR_RECURSE_DIRS;
7808f90673Sjfb 			break;
7908f90673Sjfb 		case 'm':
80f4d74cd1Stobias 			/* free previously assigned value */
81f4d74cd1Stobias 			if (logmsg != NULL)
82f4d74cd1Stobias 				xfree(logmsg);
833ad3fb45Sjoris 			logmsg = xstrdup(optarg);
84f4d74cd1Stobias 			mflag = 1;
8508f90673Sjfb 			break;
863ad3fb45Sjoris 		case 'R':
87bcf22459Stobias 			flags |= CR_RECURSE_DIRS;
888d550b2eSxsa 			break;
896f8f2f56Sxsa 		case 'r':
906f8f2f56Sxsa 			break;
9108f90673Sjfb 		default:
923ad3fb45Sjoris 			fatal("%s", cvs_cmd_commit.cmd_synopsis);
9308f90673Sjfb 		}
9408f90673Sjfb 	}
9508f90673Sjfb 
963ad3fb45Sjoris 	argc -= optind;
973ad3fb45Sjoris 	argv += optind;
9808f90673Sjfb 
99f4d74cd1Stobias 	/* -F and -m are mutually exclusive */
100f4d74cd1Stobias 	if (Fflag && mflag)
101f4d74cd1Stobias 		fatal("cannot specify both a log file and a message");
102f4d74cd1Stobias 
103db758d52Sjoris 	TAILQ_INIT(&files_affected);
104db758d52Sjoris 	TAILQ_INIT(&files_added);
105db758d52Sjoris 	TAILQ_INIT(&files_removed);
106db758d52Sjoris 	TAILQ_INIT(&files_modified);
107db758d52Sjoris 	conflicts_found = 0;
10808f90673Sjfb 
1099fac60a5Sjoris 	cr.enterdir = NULL;
1109fac60a5Sjoris 	cr.leavedir = NULL;
111db758d52Sjoris 	cr.fileproc = cvs_commit_check_files;
1129fac60a5Sjoris 	cr.flags = flags;
1139fac60a5Sjoris 
1149fac60a5Sjoris 	if (argc > 0)
1159fac60a5Sjoris 		cvs_file_run(argc, argv, &cr);
1169fac60a5Sjoris 	else
1179fac60a5Sjoris 		cvs_file_run(1, &arg, &cr);
1189fac60a5Sjoris 
119db758d52Sjoris 	if (conflicts_found != 0)
120db758d52Sjoris 		fatal("%d conflicts found, please correct these first",
121db758d52Sjoris 		    conflicts_found);
122db758d52Sjoris 
123a066adb3Sjoris 	if (TAILQ_EMPTY(&files_affected))
124a066adb3Sjoris 		return (0);
125a066adb3Sjoris 
126db758d52Sjoris 	if (logmsg == NULL && cvs_server_active == 0) {
127db758d52Sjoris 		logmsg = cvs_logmsg_create(&files_added, &files_removed,
128db758d52Sjoris 		    &files_modified);
129db758d52Sjoris 	}
130db758d52Sjoris 
131db758d52Sjoris 	if (logmsg == NULL)
132db758d52Sjoris 		fatal("This shouldnt happen, honestly!");
133db758d52Sjoris 
1344c65ce21Sjoris 	cvs_file_freelist(&files_modified);
1354c65ce21Sjoris 	cvs_file_freelist(&files_removed);
1364c65ce21Sjoris 	cvs_file_freelist(&files_added);
1374c65ce21Sjoris 
138db758d52Sjoris 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
13980f6ca9bSjoris 		cvs_client_connect_to_server();
140db758d52Sjoris 		cr.fileproc = cvs_client_sendfile;
141db758d52Sjoris 
142db758d52Sjoris 		if (argc > 0)
143db758d52Sjoris 			cvs_file_run(argc, argv, &cr);
144db758d52Sjoris 		else
145db758d52Sjoris 			cvs_file_run(1, &arg, &cr);
146db758d52Sjoris 
147585a1783Sxsa 		if (!(flags & CR_RECURSE_DIRS))
148585a1783Sxsa 			cvs_client_send_request("Argument -l");
149585a1783Sxsa 
150f76ce13cSjoris 		cvs_client_send_logmsg(logmsg);
1519fac60a5Sjoris 		cvs_client_send_files(argv, argc);
1529fac60a5Sjoris 		cvs_client_senddir(".");
1539fac60a5Sjoris 		cvs_client_send_request("ci");
1549fac60a5Sjoris 		cvs_client_get_responses();
155db758d52Sjoris 	} else {
156bc5d89feSjoris 		cr.fileproc = cvs_commit_local;
1573ad3fb45Sjoris 		cvs_file_walklist(&files_affected, &cr);
1583ad3fb45Sjoris 		cvs_file_freelist(&files_affected);
159db758d52Sjoris 	}
160ef6e385cSjoris 
16154de3f21Stobias 	xfree(logmsg);
16216cfc147Sjoris 	return (0);
163c710bc5aSjfb }
164c710bc5aSjfb 
1653ad3fb45Sjoris void
166db758d52Sjoris cvs_commit_check_files(struct cvs_file *cf)
16716cfc147Sjoris {
16818cf7829Sjoris 	char *tag;
16918cf7829Sjoris 	RCSNUM *branch, *brev;
17018cf7829Sjoris 	char rev[CVS_REV_BUFSZ];
17118cf7829Sjoris 
17254de3f21Stobias 	branch = brev = NULL;
17354de3f21Stobias 
174db758d52Sjoris 	cvs_log(LP_TRACE, "cvs_commit_check_files(%s)", cf->file_path);
1755805c4b0Sjoris 
1767cffd7e4Sjoris 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL)
1777cffd7e4Sjoris 		cvs_remote_classify_file(cf);
1787cffd7e4Sjoris 	else
17951ef6581Sjoris 		cvs_file_classify(cf, cvs_directory_tag);
1803ad3fb45Sjoris 
181c1fe09b4Sjoris 	if (cf->file_type == CVS_DIR) {
182c1fe09b4Sjoris 		if (verbosity > 1)
183c1fe09b4Sjoris 			cvs_log(LP_NOTICE, "Examining %s", cf->file_path);
184c1fe09b4Sjoris 		return;
185c1fe09b4Sjoris 	}
186c1fe09b4Sjoris 
1873ad3fb45Sjoris 	if (cf->file_status == FILE_CONFLICT ||
188846e2b74Sjoris 	    cf->file_status == FILE_UNLINK) {
1893ad3fb45Sjoris 		conflicts_found++;
190846e2b74Sjoris 		return;
191846e2b74Sjoris 	}
1923ad3fb45Sjoris 
19319615a73Sjoris 	if (cf->file_status != FILE_REMOVED &&
19419615a73Sjoris 	    update_has_conflict_markers(cf)) {
195f9872b43Sjoris 		cvs_log(LP_ERR, "conflict: unresolved conflicts in %s from "
196f9872b43Sjoris 		    "merging, please fix these first", cf->file_path);
197f9872b43Sjoris 		conflicts_found++;
198846e2b74Sjoris 		return;
199f9872b43Sjoris 	}
200f9872b43Sjoris 
2012a132f99Sjoris 	if (cf->file_status == FILE_MERGE ||
202d1eec7e9Sjoris 	    cf->file_status == FILE_PATCH ||
203846e2b74Sjoris 	    cf->file_status == FILE_CHECKOUT ||
204846e2b74Sjoris 	    cf->file_status == FILE_LOST) {
2052a132f99Sjoris 		cvs_log(LP_ERR, "conflict: %s is not up-to-date",
2062a132f99Sjoris 		    cf->file_path);
2072a132f99Sjoris 		conflicts_found++;
208846e2b74Sjoris 		return;
2092a132f99Sjoris 	}
2102a132f99Sjoris 
21118cf7829Sjoris 	if (current_cvsroot->cr_method == CVS_METHOD_LOCAL) {
21218cf7829Sjoris 		tag = cvs_directory_tag;
21318cf7829Sjoris 		if (cf->file_ent != NULL)
21418cf7829Sjoris 			tag = cf->file_ent->ce_tag;
21518cf7829Sjoris 
21618cf7829Sjoris 		if (tag != NULL && cf->file_rcs != NULL) {
21718cf7829Sjoris 			brev = rcs_sym_getrev(cf->file_rcs, tag);
21818cf7829Sjoris 			if (brev != NULL) {
21918cf7829Sjoris 				if (RCSNUM_ISBRANCH(brev))
22018cf7829Sjoris 					goto next;
2214649b416Sjoris 				rcsnum_free(brev);
22218cf7829Sjoris 			}
22318cf7829Sjoris 
22418cf7829Sjoris 			brev = rcs_translate_tag(tag, cf->file_rcs);
22518cf7829Sjoris 
22618cf7829Sjoris 			if (brev == NULL) {
22718cf7829Sjoris 				fatal("failed to resolve tag: %s",
22818cf7829Sjoris 				    cf->file_ent->ce_tag);
22918cf7829Sjoris 			}
23018cf7829Sjoris 
23118cf7829Sjoris 			rcsnum_tostr(brev, rev, sizeof(rev));
23218cf7829Sjoris 			if ((branch = rcsnum_revtobr(brev)) == NULL) {
23318cf7829Sjoris 				cvs_log(LP_ERR, "%s is not a branch revision",
23418cf7829Sjoris 				    rev);
23518cf7829Sjoris 				conflicts_found++;
23654de3f21Stobias 				rcsnum_free(brev);
23718cf7829Sjoris 				return;
23818cf7829Sjoris 			}
23918cf7829Sjoris 
24018cf7829Sjoris 			if (!RCSNUM_ISBRANCHREV(brev)) {
24118cf7829Sjoris 				cvs_log(LP_ERR, "%s is not a branch revision",
24218cf7829Sjoris 				    rev);
24318cf7829Sjoris 				conflicts_found++;
24454de3f21Stobias 				rcsnum_free(branch);
24554de3f21Stobias 				rcsnum_free(brev);
24618cf7829Sjoris 				return;
24718cf7829Sjoris 			}
24818cf7829Sjoris 
24918cf7829Sjoris 			rcsnum_tostr(branch, rev, sizeof(rev));
25018cf7829Sjoris 			if (!RCSNUM_ISBRANCH(branch)) {
25118cf7829Sjoris 				cvs_log(LP_ERR, "%s (%s) is not a branch",
25218cf7829Sjoris 				    cf->file_ent->ce_tag, rev);
25318cf7829Sjoris 				conflicts_found++;
25454de3f21Stobias 				rcsnum_free(branch);
25554de3f21Stobias 				rcsnum_free(brev);
25618cf7829Sjoris 				return;
25718cf7829Sjoris 			}
25818cf7829Sjoris 		}
25918cf7829Sjoris 	}
26018cf7829Sjoris 
26118cf7829Sjoris next:
26254de3f21Stobias 	if (branch != NULL)
26354de3f21Stobias 		rcsnum_free(branch);
26454de3f21Stobias 	if (brev != NULL)
26554de3f21Stobias 		rcsnum_free(brev);
26654de3f21Stobias 
2673ad3fb45Sjoris 	if (cf->file_status == FILE_ADDED ||
2683ad3fb45Sjoris 	    cf->file_status == FILE_REMOVED ||
2693ad3fb45Sjoris 	    cf->file_status == FILE_MODIFIED)
27051ef6581Sjoris 		cvs_file_get(cf->file_path, 0, &files_affected);
271db758d52Sjoris 
272db758d52Sjoris 	switch (cf->file_status) {
273db758d52Sjoris 	case FILE_ADDED:
27451ef6581Sjoris 		cvs_file_get(cf->file_path, 0, &files_added);
275db758d52Sjoris 		break;
276db758d52Sjoris 	case FILE_REMOVED:
27751ef6581Sjoris 		cvs_file_get(cf->file_path, 0, &files_removed);
278db758d52Sjoris 		break;
279db758d52Sjoris 	case FILE_MODIFIED:
28051ef6581Sjoris 		cvs_file_get(cf->file_path, 0, &files_modified);
281db758d52Sjoris 		break;
282db758d52Sjoris 	}
283ef6e385cSjoris }
284ef6e385cSjoris 
2853ad3fb45Sjoris void
2863ad3fb45Sjoris cvs_commit_local(struct cvs_file *cf)
287213afef1Sjfb {
28818cf7829Sjoris 	char *tag;
2894d33f394Sjoris 	BUF *b, *d;
29018cf7829Sjoris 	int onbranch, isnew, histtype;
29118cf7829Sjoris 	RCSNUM *nrev, *crev, *rrev, *brev;
292c486465dSxsa 	int openflags, rcsflags;
2937fa064caStobias 	char rbuf[CVS_REV_BUFSZ], nbuf[CVS_REV_BUFSZ];
294d77e7b73Sjoris 	CVSENTRIES *entlist;
295ba7b4b60Sotto 	char attic[MAXPATHLEN], repo[MAXPATHLEN], rcsfile[MAXPATHLEN];
296213afef1Sjfb 
2973ad3fb45Sjoris 	cvs_log(LP_TRACE, "cvs_commit_local(%s)", cf->file_path);
29851ef6581Sjoris 	cvs_file_classify(cf, cvs_directory_tag);
299213afef1Sjfb 
3007938e528Sjoris 	if (cvs_noexec == 1)
3017938e528Sjoris 		return;
3027938e528Sjoris 
3037fcaca70Sjoris 	if (cf->file_type != CVS_FILE)
3047fcaca70Sjoris 		fatal("cvs_commit_local: '%s' is not a file", cf->file_path);
3057fcaca70Sjoris 
3063901dfa5Sjoris 	if (cf->file_status != FILE_MODIFIED &&
3073901dfa5Sjoris 	    cf->file_status != FILE_ADDED &&
3083901dfa5Sjoris 	    cf->file_status != FILE_REMOVED) {
3093901dfa5Sjoris 		cvs_log(LP_ERR, "skipping bogus file `%s'", cf->file_path);
3103901dfa5Sjoris 		return;
3113901dfa5Sjoris 	}
3123901dfa5Sjoris 
31318cf7829Sjoris 	onbranch = 0;
31418cf7829Sjoris 	nrev = RCS_HEAD_REV;
31518cf7829Sjoris 	crev = NULL;
31618cf7829Sjoris 	rrev = NULL;
31718cf7829Sjoris 
318eb6d38a6Sjoris 	if (cf->file_rcs != NULL && cf->file_rcs->rf_branch != NULL) {
319b6bee913Sjoris 		rcsnum_free(cf->file_rcs->rf_branch);
320b6bee913Sjoris 		cf->file_rcs->rf_branch = NULL;
321b6bee913Sjoris 	}
322b6bee913Sjoris 
323d77e7b73Sjoris 	if (cf->file_status == FILE_MODIFIED ||
32461e51155Sjoris 	    cf->file_status == FILE_REMOVED || (cf->file_status == FILE_ADDED
325570941ffSjoris 	    && cf->file_rcs != NULL && cf->file_rcs->rf_dead == 1)) {
32618cf7829Sjoris 		rrev = rcs_head_get(cf->file_rcs);
32718cf7829Sjoris 		crev = rcs_head_get(cf->file_rcs);
3289af0ab72Stobias 		if (crev == NULL || rrev == NULL)
3299af0ab72Stobias 			fatal("RCS head empty or missing in %s\n",
3309af0ab72Stobias 			    cf->file_rcs->rf_path);
33118cf7829Sjoris 
33218cf7829Sjoris 		tag = cvs_directory_tag;
33318cf7829Sjoris 		if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL)
33418cf7829Sjoris 			tag = cf->file_ent->ce_tag;
33518cf7829Sjoris 
33618cf7829Sjoris 		if (tag != NULL) {
33718cf7829Sjoris 			rcsnum_free(crev);
33818cf7829Sjoris 			crev = rcs_translate_tag(tag, cf->file_rcs);
33918cf7829Sjoris 			if (crev == NULL) {
34018cf7829Sjoris 				fatal("failed to resolve existing tag: %s",
34118cf7829Sjoris 				    tag);
34218cf7829Sjoris 			}
34318cf7829Sjoris 
34418cf7829Sjoris 			if (RCSNUM_ISBRANCHREV(crev)) {
34518cf7829Sjoris 				nrev = rcsnum_alloc();
34618cf7829Sjoris 				rcsnum_cpy(crev, nrev, 0);
34718cf7829Sjoris 				rcsnum_inc(nrev);
34818cf7829Sjoris 			} else if (!RCSNUM_ISBRANCH(crev)) {
34918cf7829Sjoris 				brev = rcs_sym_getrev(cf->file_rcs, tag);
35018cf7829Sjoris 				if (brev == NULL)
35118cf7829Sjoris 					fatal("no more tag?");
35218cf7829Sjoris 				nrev = rcsnum_brtorev(brev);
35318cf7829Sjoris 				if (nrev == NULL)
35418cf7829Sjoris 					fatal("failed to create branch rev");
35554de3f21Stobias 				rcsnum_free(brev);
35618cf7829Sjoris 			} else {
35718cf7829Sjoris 				fatal("this isnt suppose to happen, honestly");
35818cf7829Sjoris 			}
35918cf7829Sjoris 
36018cf7829Sjoris 			rcsnum_free(rrev);
36118cf7829Sjoris 			rrev = rcsnum_branch_root(nrev);
36218cf7829Sjoris 
36318cf7829Sjoris 			/* branch stuff was checked in cvs_commit_check_files */
36418cf7829Sjoris 			onbranch = 1;
36518cf7829Sjoris 		}
36618cf7829Sjoris 
36718cf7829Sjoris 		rcsnum_tostr(crev, rbuf, sizeof(rbuf));
368570941ffSjoris 	} else {
3696bbad201Sjoris 		strlcpy(rbuf, "Non-existent", sizeof(rbuf));
370570941ffSjoris 	}
3716bbad201Sjoris 
37254de3f21Stobias 	if (rrev != NULL)
37354de3f21Stobias 		rcsnum_free(rrev);
37443c5228fSjoris 	isnew = 0;
37561e51155Sjoris 	if (cf->file_status == FILE_ADDED) {
37643c5228fSjoris 		isnew = 1;
37761e51155Sjoris 		rcsflags = RCS_CREATE;
37861e51155Sjoris 		openflags = O_CREAT | O_TRUNC | O_WRONLY;
37961e51155Sjoris 		if (cf->file_rcs != NULL) {
380ca2dc546Sniallo 			if (cf->in_attic == 0)
38161e51155Sjoris 				cvs_log(LP_ERR, "warning: expected %s "
38261e51155Sjoris 				    "to be in the Attic", cf->file_path);
38361e51155Sjoris 
38461e51155Sjoris 			if (cf->file_rcs->rf_dead == 0)
38561e51155Sjoris 				cvs_log(LP_ERR, "warning: expected %s "
38661e51155Sjoris 				    "to be dead", cf->file_path);
38761e51155Sjoris 
38861e51155Sjoris 			cvs_get_repository_path(cf->file_wd, repo, MAXPATHLEN);
389c486465dSxsa 			(void)xsnprintf(rcsfile, MAXPATHLEN, "%s/%s%s",
39061e51155Sjoris 			    repo, cf->file_name, RCS_FILE_EXT);
39161e51155Sjoris 
39261e51155Sjoris 			if (rename(cf->file_rpath, rcsfile) == -1)
39361e51155Sjoris 				fatal("cvs_commit_local: failed to move %s "
39461e51155Sjoris 				    "outside the Attic: %s", cf->file_path,
39561e51155Sjoris 				    strerror(errno));
39661e51155Sjoris 
39761e51155Sjoris 			xfree(cf->file_rpath);
39861e51155Sjoris 			cf->file_rpath = xstrdup(rcsfile);
39961e51155Sjoris 
40061e51155Sjoris 			rcsflags = RCS_READ | RCS_PARSE_FULLY;
40161e51155Sjoris 			openflags = O_RDONLY;
40261e51155Sjoris 			rcs_close(cf->file_rcs);
40343c5228fSjoris 			isnew = 0;
40461e51155Sjoris 		}
40561e51155Sjoris 
40661e51155Sjoris 		cf->repo_fd = open(cf->file_rpath, openflags);
4076bbad201Sjoris 		if (cf->repo_fd < 0)
4086bbad201Sjoris 			fatal("cvs_commit_local: %s", strerror(errno));
4096bbad201Sjoris 
4106bbad201Sjoris 		cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd,
411998f3c00Sotto 		    rcsflags, 0444);
4126bbad201Sjoris 		if (cf->file_rcs == NULL)
4136bbad201Sjoris 			fatal("cvs_commit_local: failed to create RCS file "
4146bbad201Sjoris 			    "for %s", cf->file_path);
4152ec286b3Sxsa 
4162ec286b3Sxsa 		commit_desc_set(cf);
4176bbad201Sjoris 	}
4183ad3fb45Sjoris 
4192868ee63Sreyk 	if (verbosity > 1) {
4203ad3fb45Sjoris 		cvs_printf("Checking in %s:\n", cf->file_path);
4213ad3fb45Sjoris 		cvs_printf("%s <- %s\n", cf->file_rpath, cf->file_path);
4223ad3fb45Sjoris 		cvs_printf("old revision: %s; ", rbuf);
4232868ee63Sreyk 	}
4243ad3fb45Sjoris 
42518cf7829Sjoris 	if (isnew == 0 && onbranch == 0)
42618cf7829Sjoris 		d = commit_diff(cf, cf->file_rcs->rf_head, 0);
4273ad3fb45Sjoris 
428d77e7b73Sjoris 	if (cf->file_status == FILE_REMOVED) {
42918cf7829Sjoris 		b = rcs_rev_getbuf(cf->file_rcs, crev, 0);
430d77e7b73Sjoris 		if (b == NULL)
43118cf7829Sjoris 			fatal("cvs_commit_local: failed to get crev");
43218cf7829Sjoris 	} else if (onbranch == 1) {
43318cf7829Sjoris 		b = commit_diff(cf, crev, 1);
4346bbad201Sjoris 	} else {
435f2a4882aSjoris 		if ((b = cvs_buf_load_fd(cf->fd, BUF_AUTOEXT)) == NULL)
4363ad3fb45Sjoris 			fatal("cvs_commit_local: failed to load file");
437d77e7b73Sjoris 	}
4383ad3fb45Sjoris 
43918cf7829Sjoris 	if (isnew == 0 && onbranch == 0) {
44018cf7829Sjoris 		if (rcs_deltatext_set(cf->file_rcs, crev, d) == -1)
4413ad3fb45Sjoris 			fatal("cvs_commit_local: failed to set delta");
4426bbad201Sjoris 	}
4433ad3fb45Sjoris 
44418cf7829Sjoris 	if (rcs_rev_add(cf->file_rcs, nrev, logmsg, -1, NULL) == -1)
4453ad3fb45Sjoris 		fatal("cvs_commit_local: failed to add new revision");
4463ad3fb45Sjoris 
44718cf7829Sjoris 	if (nrev == RCS_HEAD_REV)
44818cf7829Sjoris 		nrev = cf->file_rcs->rf_head;
44918cf7829Sjoris 
45018cf7829Sjoris 	if (rcs_deltatext_set(cf->file_rcs, nrev, b) == -1)
4513ad3fb45Sjoris 		fatal("cvs_commit_local: failed to set new HEAD delta");
4523ad3fb45Sjoris 
453d77e7b73Sjoris 	if (cf->file_status == FILE_REMOVED) {
45418cf7829Sjoris 		if (rcs_state_set(cf->file_rcs, nrev, RCS_STATE_DEAD) == -1)
455d77e7b73Sjoris 			fatal("cvs_commit_local: failed to set state");
456d77e7b73Sjoris 	}
457d77e7b73Sjoris 
45856f996a2Sxsa 	if (cf->file_status == FILE_ADDED && cf->file_ent->ce_opts != NULL) {
459*37fdff3fStobias 		int cf_kflag;
46056f996a2Sxsa 
461*37fdff3fStobias 		cf_kflag = rcs_kflag_get(cf->file_ent->ce_opts + 2);
462*37fdff3fStobias 		rcs_kwexp_set(cf->file_rcs, cf_kflag);
46356f996a2Sxsa 	}
46456f996a2Sxsa 
4653ad3fb45Sjoris 	rcs_write(cf->file_rcs);
4663ad3fb45Sjoris 
467d77e7b73Sjoris 	if (cf->file_status == FILE_REMOVED) {
4682868ee63Sreyk 		strlcpy(nbuf, "Removed", sizeof(nbuf));
4696bbad201Sjoris 	} else if (cf->file_status == FILE_ADDED) {
47061e51155Sjoris 		if (cf->file_rcs->rf_dead == 1)
4712868ee63Sreyk 			strlcpy(nbuf, "Initial Revision", sizeof(nbuf));
47219615a73Sjoris 		else
47318cf7829Sjoris 			rcsnum_tostr(nrev, nbuf, sizeof(nbuf));
474d77e7b73Sjoris 	} else if (cf->file_status == FILE_MODIFIED) {
47518cf7829Sjoris 		rcsnum_tostr(nrev, nbuf, sizeof(nbuf));
476d77e7b73Sjoris 	}
477d77e7b73Sjoris 
4782868ee63Sreyk 	if (verbosity > 1)
4792868ee63Sreyk 		cvs_printf("new revision: %s\n", nbuf);
4803ad3fb45Sjoris 
4813ad3fb45Sjoris 	(void)unlink(cf->file_path);
4823ad3fb45Sjoris 	(void)close(cf->fd);
4833ad3fb45Sjoris 	cf->fd = -1;
484d77e7b73Sjoris 
485d77e7b73Sjoris 	if (cf->file_status != FILE_REMOVED) {
48618cf7829Sjoris 		cvs_checkout_file(cf, nrev, NULL, CO_COMMIT);
487d77e7b73Sjoris 	} else {
488d77e7b73Sjoris 		entlist = cvs_ent_open(cf->file_wd);
489d77e7b73Sjoris 		cvs_ent_remove(entlist, cf->file_name);
490d77e7b73Sjoris 		cvs_ent_close(entlist, ENT_SYNC);
4917fcaca70Sjoris 
4927fcaca70Sjoris 		cvs_get_repository_path(cf->file_wd, repo, MAXPATHLEN);
4937fcaca70Sjoris 
494c486465dSxsa 		(void)xsnprintf(attic, MAXPATHLEN, "%s/%s",
495c486465dSxsa 		    repo, CVS_PATH_ATTIC);
4967fcaca70Sjoris 
4977fcaca70Sjoris 		if (mkdir(attic, 0755) == -1 && errno != EEXIST)
4987fcaca70Sjoris 			fatal("cvs_commit_local: failed to create Attic");
4997fcaca70Sjoris 
500c486465dSxsa 		(void)xsnprintf(attic, MAXPATHLEN, "%s/%s/%s%s", repo,
5017fcaca70Sjoris 		    CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT);
5027fcaca70Sjoris 
5037fcaca70Sjoris 		if (rename(cf->file_rpath, attic) == -1)
5047fcaca70Sjoris 			fatal("cvs_commit_local: failed to move %s to Attic",
5057fcaca70Sjoris 			    cf->file_path);
5067fcaca70Sjoris 
507408908afSjoris 		if (cvs_server_active == 1)
508408908afSjoris 			cvs_server_update_entry("Remove-entry", cf);
509d77e7b73Sjoris 	}
5103ad3fb45Sjoris 
5112868ee63Sreyk 	if (verbosity > 1)
5123ad3fb45Sjoris 		cvs_printf("done\n");
5132868ee63Sreyk 	else {
5142868ee63Sreyk 		cvs_log(LP_NOTICE, "checking in '%s'; revision %s -> %s",
5152868ee63Sreyk 		    cf->file_path, rbuf, nbuf);
5162868ee63Sreyk 	}
5173901dfa5Sjoris 
5183901dfa5Sjoris 	switch (cf->file_status) {
5193901dfa5Sjoris 	case FILE_MODIFIED:
5203901dfa5Sjoris 		histtype = CVS_HISTORY_COMMIT_MODIFIED;
5213901dfa5Sjoris 		break;
5223901dfa5Sjoris 	case FILE_ADDED:
5233901dfa5Sjoris 		histtype = CVS_HISTORY_COMMIT_ADDED;
5243901dfa5Sjoris 		break;
5253901dfa5Sjoris 	case FILE_REMOVED:
5263901dfa5Sjoris 		histtype = CVS_HISTORY_COMMIT_REMOVED;
5273901dfa5Sjoris 		break;
5283901dfa5Sjoris 	}
5293901dfa5Sjoris 
5304649b416Sjoris 	if (crev != NULL)
5314649b416Sjoris 		rcsnum_free(crev);
5324649b416Sjoris 
5333901dfa5Sjoris 	cvs_history_add(histtype, cf, NULL);
534213afef1Sjfb }
535c710bc5aSjfb 
5364d33f394Sjoris static BUF *
53718cf7829Sjoris commit_diff(struct cvs_file *cf, RCSNUM *rev, int reverse)
53808f90673Sjfb {
53918cf7829Sjoris 	char *p1, *p2, *p;
540b794e5f5Stobias 	BUF *b;
541924e91c6Sjoris 
542924e91c6Sjoris 	(void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
54308f90673Sjfb 
54419615a73Sjoris 	if (cf->file_status == FILE_MODIFIED ||
54519615a73Sjoris 	    cf->file_status == FILE_ADDED) {
546b794e5f5Stobias 		if ((b = cvs_buf_load_fd(cf->fd, BUF_AUTOEXT)) == NULL)
54717b2872aStobias 			fatal("commit_diff: failed to load '%s'",
548d77e7b73Sjoris 			    cf->file_path);
549b794e5f5Stobias 		cvs_buf_write_stmp(b, p1, NULL);
550b794e5f5Stobias 		cvs_buf_free(b);
551924e91c6Sjoris 	} else {
55218cf7829Sjoris 		rcs_rev_write_stmp(cf->file_rcs, rev, p1, 0);
553924e91c6Sjoris 	}
5546169aed9Skrapht 
5553ad3fb45Sjoris 	(void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
55687d368dcStobias 	rcs_rev_write_stmp(cf->file_rcs, rev, p2, RCS_KWEXP_NONE);
557924e91c6Sjoris 
558b794e5f5Stobias 	if ((b = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL)
55918cf7829Sjoris 		fatal("commit_diff: failed to create diff buf");
56038c61fc2Sjoris 
5613ad3fb45Sjoris 	diff_format = D_RCSDIFF;
56218cf7829Sjoris 
56318cf7829Sjoris 	if (reverse == 1) {
56418cf7829Sjoris 		p = p1;
56518cf7829Sjoris 		p1 = p2;
56618cf7829Sjoris 		p2 = p;
56718cf7829Sjoris 	}
56818cf7829Sjoris 
569b794e5f5Stobias 	if (cvs_diffreg(p1, p2, b) == D_ERROR)
57018cf7829Sjoris 		fatal("commit_diff: failed to get RCS patch");
571740595fcSxsa 
5728787a230Sjoris 	xfree(p1);
5738787a230Sjoris 	xfree(p2);
5748787a230Sjoris 
575b794e5f5Stobias 	return (b);
5766169aed9Skrapht }
5772ec286b3Sxsa 
5782ec286b3Sxsa static void
5792ec286b3Sxsa commit_desc_set(struct cvs_file *cf)
5802ec286b3Sxsa {
5812ec286b3Sxsa 	BUF *bp;
582c486465dSxsa 	int fd;
583ba7b4b60Sotto 	char desc_path[MAXPATHLEN], *desc;
5842ec286b3Sxsa 
585c486465dSxsa 	(void)xsnprintf(desc_path, MAXPATHLEN, "%s/%s%s",
5862ec286b3Sxsa 	    CVS_PATH_CVSDIR, cf->file_name, CVS_DESCR_FILE_EXT);
5872ec286b3Sxsa 
588ba7b4b60Sotto 	if ((fd = open(desc_path, O_RDONLY)) == -1)
5892ec286b3Sxsa 		return;
5902ec286b3Sxsa 
591fe6c66d1Sjoris 	bp = cvs_buf_load_fd(fd, BUF_AUTOEXT);
5922ec286b3Sxsa 	cvs_buf_putc(bp, '\0');
5932ec286b3Sxsa 	desc = cvs_buf_release(bp);
5942ec286b3Sxsa 
5952ec286b3Sxsa 	rcs_desc_set(cf->file_rcs, desc);
5962ec286b3Sxsa 
5972d02866cSjoris 	(void)close(fd);
5982ec286b3Sxsa 	(void)cvs_unlink(desc_path);
5992ec286b3Sxsa 
6002ec286b3Sxsa 	xfree(desc);
6012ec286b3Sxsa }
602