xref: /openbsd/usr.bin/cvs/commit.c (revision 3aaa63eb)
1 /*	$OpenBSD: commit.c,v 1.160 2019/06/28 13:35:00 deraadt Exp $	*/
2 /*
3  * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
4  * Copyright (c) 2006 Xavier Santolaria <xsa@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/stat.h>
20 
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #include "cvs.h"
28 #include "diff.h"
29 #include "remote.h"
30 
31 void			 cvs_commit_local(struct cvs_file *);
32 void			 cvs_commit_check_files(struct cvs_file *);
33 void			 cvs_commit_loginfo(char *);
34 void			 cvs_commit_lock_dirs(struct cvs_file *);
35 
36 static BUF *commit_diff(struct cvs_file *, RCSNUM *, int);
37 static void commit_desc_set(struct cvs_file *);
38 
39 struct	file_info_list	 files_info;
40 struct	trigger_list	*line_list;
41 
42 struct cvs_flisthead	files_affected;
43 struct cvs_flisthead	files_added;
44 struct cvs_flisthead	files_removed;
45 struct cvs_flisthead	files_modified;
46 
47 char	*logmsg = NULL;
48 char	*loginfo = NULL;
49 
50 static int	conflicts_found;
51 
52 struct cvs_cmd cvs_cmd_commit = {
53 	CVS_OP_COMMIT, CVS_USE_WDIR, "commit",
54 	{ "ci", "com" },
55 	"Check files into the repository",
56 	"[-flR] [-F logfile | -m msg] [-r rev] ...",
57 	"F:flm:Rr:",
58 	NULL,
59 	cvs_commit
60 };
61 
62 int
cvs_commit(int argc,char ** argv)63 cvs_commit(int argc, char **argv)
64 {
65 	int flags;
66 	int ch, Fflag, mflag;
67 	struct module_checkout *mc;
68 	struct cvs_recursion cr;
69 	struct cvs_filelist *l;
70 	struct file_info *fi;
71 	char *arg = ".", repo[PATH_MAX];
72 
73 	flags = CR_RECURSE_DIRS;
74 	Fflag = mflag = 0;
75 
76 	while ((ch = getopt(argc, argv, cvs_cmd_commit.cmd_opts)) != -1) {
77 		switch (ch) {
78 		case 'F':
79 			/* free previously assigned value */
80 			free(logmsg);
81 			logmsg = cvs_logmsg_read(optarg);
82 			Fflag = 1;
83 			break;
84 		case 'f':
85 			break;
86 		case 'l':
87 			flags &= ~CR_RECURSE_DIRS;
88 			break;
89 		case 'm':
90 			/* free previously assigned value */
91 			free(logmsg);
92 			logmsg = xstrdup(optarg);
93 			mflag = 1;
94 			break;
95 		case 'R':
96 			flags |= CR_RECURSE_DIRS;
97 			break;
98 		case 'r':
99 			break;
100 		default:
101 			fatal("%s", cvs_cmd_commit.cmd_synopsis);
102 		}
103 	}
104 
105 	argc -= optind;
106 	argv += optind;
107 
108 	/* -F and -m are mutually exclusive */
109 	if (Fflag && mflag)
110 		fatal("cannot specify both a log file and a message");
111 
112 	RB_INIT(&files_affected);
113 	RB_INIT(&files_added);
114 	RB_INIT(&files_removed);
115 	RB_INIT(&files_modified);
116 
117 	TAILQ_INIT(&files_info);
118 	conflicts_found = 0;
119 
120 	cr.enterdir = NULL;
121 	cr.leavedir = NULL;
122 	cr.fileproc = cvs_commit_check_files;
123 	cr.flags = flags;
124 
125 	if (argc > 0)
126 		cvs_file_run(argc, argv, &cr);
127 	else
128 		cvs_file_run(1, &arg, &cr);
129 
130 	if (conflicts_found != 0)
131 		fatal("%d conflicts found, please correct these first",
132 		    conflicts_found);
133 
134 	if (RB_EMPTY(&files_affected))
135 		return (0);
136 
137 	if (cvsroot_is_remote()) {
138 		if (logmsg == NULL) {
139 			logmsg = cvs_logmsg_create(NULL, &files_added,
140 			    &files_removed, &files_modified);
141 			if (logmsg == NULL)
142 				fatal("This shouldnt happen, honestly!");
143 		}
144 		cvs_client_connect_to_server();
145 		cr.fileproc = cvs_client_sendfile;
146 
147 		if (argc > 0)
148 			cvs_file_run(argc, argv, &cr);
149 		else
150 			cvs_file_run(1, &arg, &cr);
151 
152 		if (!(flags & CR_RECURSE_DIRS))
153 			cvs_client_send_request("Argument -l");
154 
155 		cvs_client_send_logmsg(logmsg);
156 		cvs_client_send_files(argv, argc);
157 		cvs_client_senddir(".");
158 		cvs_client_send_request("ci");
159 		cvs_client_get_responses();
160 	} else {
161 		cvs_get_repository_name(".", repo, PATH_MAX);
162 
163 		line_list = cvs_trigger_getlines(CVS_PATH_COMMITINFO, repo);
164 		if (line_list != NULL) {
165 			RB_FOREACH(l, cvs_flisthead, &files_affected) {
166 				fi = xcalloc(1, sizeof(*fi));
167 				fi->file_path = xstrdup(l->file_path);
168 				TAILQ_INSERT_TAIL(&files_info, fi,
169 				    flist);
170 			}
171 
172 			if (cvs_trigger_handle(CVS_TRIGGER_COMMITINFO,
173 			    repo, NULL, line_list, &files_info)) {
174 				cvs_log(LP_ERR,
175 				    "Pre-commit check failed");
176 				cvs_trigger_freelist(line_list);
177 				goto end;
178 			}
179 
180 			cvs_trigger_freelist(line_list);
181 			cvs_trigger_freeinfo(&files_info);
182 		}
183 
184 		if (cvs_server_active) {
185 			if (logmsg == NULL)
186 				fatal("no log message specified");
187 		} else if (logmsg == NULL) {
188 			logmsg = cvs_logmsg_create(NULL, &files_added,
189 			    &files_removed, &files_modified);
190 			if (logmsg == NULL)
191 				fatal("This shouldnt happen, honestly!");
192 		}
193 
194 		if (cvs_logmsg_verify(logmsg))
195 			goto end;
196 
197 		cr.fileproc = cvs_commit_lock_dirs;
198 		cvs_file_walklist(&files_affected, &cr);
199 
200 		line_list = cvs_trigger_getlines(CVS_PATH_LOGINFO, repo);
201 
202 		cr.fileproc = cvs_commit_local;
203 		cvs_file_walklist(&files_affected, &cr);
204 
205 		if (line_list != NULL) {
206 			cvs_commit_loginfo(repo);
207 
208 			cvs_trigger_handle(CVS_TRIGGER_LOGINFO, repo,
209 			    loginfo, line_list, &files_info);
210 
211 			free(loginfo);
212 			cvs_trigger_freelist(line_list);
213 			cvs_trigger_freeinfo(&files_info);
214 		}
215 
216 		mc = cvs_module_lookup(repo);
217 		if (mc->mc_prog != NULL &&
218 		    (mc->mc_flags & MODULE_RUN_ON_COMMIT))
219 			cvs_exec(mc->mc_prog, NULL, 0);
220 	}
221 
222 end:
223 	cvs_trigger_freeinfo(&files_info);
224 	free(logmsg);
225 	return (0);
226 }
227 
228 void
cvs_commit_loginfo(char * repo)229 cvs_commit_loginfo(char *repo)
230 {
231 	BUF *buf;
232 	char pwd[PATH_MAX];
233 	struct cvs_filelist *cf;
234 
235 	if (getcwd(pwd, sizeof(pwd)) == NULL)
236 		fatal("Can't get working directory");
237 
238 	buf = buf_alloc(1024);
239 
240 	cvs_trigger_loginfo_header(buf, repo);
241 
242 	if (!RB_EMPTY(&files_added)) {
243 		buf_puts(buf, "Added Files:");
244 
245 		RB_FOREACH(cf, cvs_flisthead, &files_added) {
246 			buf_putc(buf, '\n');
247 			buf_putc(buf, '\t');
248 			buf_puts(buf, cf->file_path);
249 		}
250 
251 		buf_putc(buf, '\n');
252 	}
253 
254 	if (!RB_EMPTY(&files_modified)) {
255 		buf_puts(buf, "Modified Files:");
256 
257 		RB_FOREACH(cf, cvs_flisthead, &files_modified) {
258 			buf_putc(buf, '\n');
259 			buf_putc(buf, '\t');
260 			buf_puts(buf, cf->file_path);
261 		}
262 
263 		buf_putc(buf, '\n');
264 	}
265 
266 	if (!RB_EMPTY(&files_removed)) {
267 		buf_puts(buf, "Removed Files:");
268 
269 		RB_FOREACH(cf, cvs_flisthead, &files_removed) {
270 			buf_putc(buf, '\n');
271 			buf_putc(buf, '\t');
272 			buf_puts(buf, cf->file_path);
273 		}
274 
275 		buf_putc(buf, '\n');
276 	}
277 
278 	buf_puts(buf, "Log Message:\n");
279 
280 	buf_puts(buf, logmsg);
281 
282 	buf_putc(buf, '\n');
283 	buf_putc(buf, '\0');
284 
285 	loginfo = buf_release(buf);
286 }
287 
288 void
cvs_commit_lock_dirs(struct cvs_file * cf)289 cvs_commit_lock_dirs(struct cvs_file *cf)
290 {
291 	char repo[PATH_MAX];
292 
293 	cvs_get_repository_path(cf->file_wd, repo, sizeof(repo));
294 	cvs_log(LP_TRACE, "cvs_commit_lock_dirs: %s", repo);
295 
296 	/* locks stay in place until we are fully done and exit */
297 	cvs_repository_lock(repo, 1);
298 }
299 
300 void
cvs_commit_check_files(struct cvs_file * cf)301 cvs_commit_check_files(struct cvs_file *cf)
302 {
303 	char *tag;
304 	RCSNUM *branch, *brev;
305 
306 	branch = brev = NULL;
307 
308 	cvs_log(LP_TRACE, "cvs_commit_check_files(%s)", cf->file_path);
309 
310 	if (cvsroot_is_remote())
311 		cvs_remote_classify_file(cf);
312 	else
313 		cvs_file_classify(cf, cvs_directory_tag);
314 
315 	if (cf->file_type == CVS_DIR) {
316 		if (verbosity > 1)
317 			cvs_log(LP_NOTICE, "Examining %s", cf->file_path);
318 		return;
319 	}
320 
321 	if (cf->file_status == FILE_UPTODATE)
322 		return;
323 
324 	if (cf->file_status == FILE_MERGE ||
325 	    cf->file_status == FILE_PATCH ||
326 	    cf->file_status == FILE_CHECKOUT ||
327 	    cf->file_status == FILE_LOST ||
328 	    cf->file_status == FILE_UNLINK) {
329 		cvs_log(LP_ERR, "conflict: %s is not up-to-date",
330 		    cf->file_path);
331 		conflicts_found++;
332 		return;
333 	}
334 
335 	if (cf->file_status == FILE_CONFLICT &&
336 	   cf->file_ent->ce_conflict != NULL) {
337 		cvs_log(LP_ERR, "conflict: unresolved conflicts in %s from "
338 		    "merging, please fix these first", cf->file_path);
339 		conflicts_found++;
340 		return;
341 	}
342 
343 	if (cf->file_status == FILE_MODIFIED &&
344 	    cf->file_ent->ce_conflict != NULL &&
345 	    update_has_conflict_markers(cf)) {
346 		cvs_log(LP_ERR, "warning: file %s seems to still contain "
347 		    "conflict indicators", cf->file_path);
348 	}
349 
350 	if (cf->file_ent != NULL && cf->file_ent->ce_date != -1) {
351 		cvs_log(LP_ERR, "conflict: cannot commit to sticky date for %s",
352 		    cf->file_path);
353 		conflicts_found++;
354 		return;
355 	}
356 
357 	if (cvsroot_is_local()) {
358 		tag = cvs_directory_tag;
359 		if (cf->file_ent != NULL)
360 			tag = cf->file_ent->ce_tag;
361 
362 		if (tag != NULL && cf->file_rcs != NULL) {
363 			brev = rcs_sym_getrev(cf->file_rcs, tag);
364 			if (brev != NULL) {
365 				if (!RCSNUM_ISBRANCH(brev)) {
366 					cvs_log(LP_ERR, "sticky tag %s is not "
367 					    "a branch for file %s", tag,
368 					    cf->file_path);
369 					conflicts_found++;
370 				}
371 			}
372 		}
373 	}
374 
375 	free(branch);
376 	free(brev);
377 
378 	if (cf->file_status != FILE_ADDED &&
379 	    cf->file_status != FILE_REMOVED &&
380 	    cf->file_status != FILE_MODIFIED)
381 		return;
382 
383 	cvs_file_get(cf->file_path, 0, &files_affected, CVS_FILE);
384 
385 	switch (cf->file_status) {
386 	case FILE_ADDED:
387 		cvs_file_get(cf->file_path, 0, &files_added, CVS_FILE);
388 		break;
389 	case FILE_REMOVED:
390 		cvs_file_get(cf->file_path, 0, &files_removed, CVS_FILE);
391 		break;
392 	case FILE_MODIFIED:
393 		cvs_file_get(cf->file_path, 0, &files_modified, CVS_FILE);
394 		break;
395 	}
396 }
397 
398 void
cvs_commit_local(struct cvs_file * cf)399 cvs_commit_local(struct cvs_file *cf)
400 {
401 	char *tag;
402 	BUF *b, *d;
403 	int onbranch, isnew, histtype, branchadded;
404 	RCSNUM *nrev, *crev, *rrev, *brev;
405 	int openflags, rcsflags;
406 	char rbuf[CVS_REV_BUFSZ], nbuf[CVS_REV_BUFSZ];
407 	CVSENTRIES *entlist;
408 	char attic[PATH_MAX], repo[PATH_MAX], rcsfile[PATH_MAX];
409 	struct file_info *fi;
410 
411 	cvs_log(LP_TRACE, "cvs_commit_local(%s)", cf->file_path);
412 	cvs_file_classify(cf, cvs_directory_tag);
413 
414 	if (cvs_noexec == 1)
415 		return;
416 
417 	if (cf->file_type != CVS_FILE)
418 		fatal("cvs_commit_local: '%s' is not a file", cf->file_path);
419 
420 	tag = cvs_directory_tag;
421 	if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL)
422 		tag = cf->file_ent->ce_tag;
423 
424 	branchadded = 0;
425 	switch (cf->file_status) {
426 	case FILE_ADDED:
427 		if (cf->file_rcs == NULL && tag != NULL) {
428 			branchadded = 1;
429 			cvs_add_tobranch(cf, tag);
430 		}
431 		break;
432 	case FILE_MODIFIED:
433 	case FILE_REMOVED:
434 		if (cf->file_rcs == NULL) {
435 			cvs_log(LP_ERR, "RCS file for %s got lost",
436 			    cf->file_path);
437 			return;
438 		}
439 		break;
440 	default:
441 		cvs_log(LP_ERR, "skipping bogus file `%s'", cf->file_path);
442 		return;
443 	}
444 
445 	onbranch = 0;
446 	nrev = RCS_HEAD_REV;
447 	crev = NULL;
448 	rrev = NULL;
449 	d = NULL;
450 
451 	if (cf->file_rcs != NULL && cf->file_rcs->rf_branch != NULL) {
452 		free(cf->file_rcs->rf_branch);
453 		cf->file_rcs->rf_branch = NULL;
454 	}
455 
456 	if (cf->file_rcs != NULL) {
457 		rrev = rcs_head_get(cf->file_rcs);
458 		crev = rcs_head_get(cf->file_rcs);
459 		if (crev == NULL || rrev == NULL)
460 			fatal("no head revision in RCS file for %s",
461 			    cf->file_path);
462 
463 		if (tag != NULL) {
464 			free(crev);
465 			free(rrev);
466 			brev = rcs_sym_getrev(cf->file_rcs, tag);
467 			crev = rcs_translate_tag(tag, cf->file_rcs);
468 			if (brev == NULL || crev == NULL) {
469 				fatal("failed to resolve existing tag: %s",
470 				    tag);
471 			}
472 
473 			rrev = rcsnum_alloc();
474 			rcsnum_cpy(brev, rrev, brev->rn_len - 1);
475 
476 			if (RCSNUM_ISBRANCHREV(crev) &&
477 			    rcsnum_cmp(crev, rrev, 0)) {
478 				nrev = rcsnum_alloc();
479 				rcsnum_cpy(crev, nrev, 0);
480 				rcsnum_inc(nrev);
481 			} else if (!RCSNUM_ISBRANCH(crev)) {
482 				nrev = rcsnum_brtorev(brev);
483 				if (nrev == NULL)
484 					fatal("failed to create branch rev");
485 			} else {
486 				fatal("this isnt suppose to happen, honestly");
487 			}
488 
489 			free(brev);
490 			free(rrev);
491 			rrev = rcsnum_branch_root(nrev);
492 
493 			/* branch stuff was checked in cvs_commit_check_files */
494 			onbranch = 1;
495 		}
496 
497 		rcsnum_tostr(crev, rbuf, sizeof(rbuf));
498 	} else {
499 		strlcpy(rbuf, "Non-existent", sizeof(rbuf));
500 	}
501 
502 	free(rrev);
503 	isnew = 0;
504 	if (cf->file_status == FILE_ADDED) {
505 		isnew = 1;
506 		rcsflags = RCS_CREATE;
507 		openflags = O_CREAT | O_RDONLY;
508 		if (cf->file_rcs != NULL) {
509 			if (!onbranch) {
510 				if (cf->in_attic == 0)
511 					cvs_log(LP_ERR, "warning: expected %s "
512 					    "to be in the Attic",
513 					    cf->file_path);
514 
515 				if (cf->file_rcs->rf_dead == 0)
516 					cvs_log(LP_ERR, "warning: expected %s "
517 					    "to be dead", cf->file_path);
518 
519 				cvs_get_repository_path(cf->file_wd, repo,
520 				    PATH_MAX);
521 				(void)xsnprintf(rcsfile, PATH_MAX, "%s/%s%s",
522 				    repo, cf->file_name, RCS_FILE_EXT);
523 
524 				if (rename(cf->file_rpath, rcsfile) == -1)
525 					fatal("cvs_commit_local: failed to "
526 					    "move %s outside the Attic: %s",
527 					    cf->file_path, strerror(errno));
528 
529 				free(cf->file_rpath);
530 				cf->file_rpath = xstrdup(rcsfile);
531 				isnew = 0;
532 			}
533 
534 			rcsflags = RCS_READ | RCS_PARSE_FULLY;
535 			openflags = O_RDONLY;
536 			rcs_close(cf->file_rcs);
537 		}
538 
539 		cf->repo_fd = open(cf->file_rpath, openflags);
540 		if (cf->repo_fd == -1)
541 			fatal("cvs_commit_local: %s", strerror(errno));
542 
543 		cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd,
544 		    rcsflags, 0444);
545 		if (cf->file_rcs == NULL)
546 			fatal("cvs_commit_local: failed to create RCS file "
547 			    "for %s", cf->file_path);
548 
549 		commit_desc_set(cf);
550 
551 		if (branchadded)
552 			strlcpy(rbuf, "Non-existent", sizeof(rbuf));
553 	}
554 
555 	if (verbosity > 1) {
556 		cvs_printf("Checking in %s:\n", cf->file_path);
557 		cvs_printf("%s <- %s\n", cf->file_rpath, cf->file_path);
558 		cvs_printf("old revision: %s; ", rbuf);
559 	}
560 
561 	if (isnew == 0 && cf->file_rcs->rf_head == NULL)
562 		fatal("no head revision in RCS file for %s", cf->file_path);
563 
564 	if (isnew == 0 && onbranch == 0)
565 		d = commit_diff(cf, cf->file_rcs->rf_head, 0);
566 
567 	if (cf->file_status == FILE_REMOVED) {
568 		b = rcs_rev_getbuf(cf->file_rcs, crev, 0);
569 	} else if (onbranch == 1) {
570 		b = commit_diff(cf, crev, 1);
571 	} else {
572 		b = buf_load_fd(cf->fd);
573 	}
574 
575 	if (isnew == 0 && onbranch == 0) {
576 		if (rcs_deltatext_set(cf->file_rcs, crev, d) == -1)
577 			fatal("cvs_commit_local: failed to set delta");
578 	}
579 
580 	if (rcs_rev_add(cf->file_rcs, nrev, logmsg, -1, NULL) == -1)
581 		fatal("cvs_commit_local: failed to add new revision");
582 
583 	if (nrev == RCS_HEAD_REV)
584 		nrev = cf->file_rcs->rf_head;
585 
586 	if (rcs_deltatext_set(cf->file_rcs, nrev, b) == -1)
587 		fatal("cvs_commit_local: failed to set new HEAD delta");
588 
589 	if (cf->file_status == FILE_REMOVED) {
590 		if (rcs_state_set(cf->file_rcs, nrev, RCS_STATE_DEAD) == -1)
591 			fatal("cvs_commit_local: failed to set state");
592 	}
593 
594 	if (cf->file_status == FILE_ADDED && cf->file_ent->ce_opts != NULL) {
595 		int cf_kflag;
596 
597 		cf_kflag = rcs_kflag_get(cf->file_ent->ce_opts + 2);
598 		rcs_kwexp_set(cf->file_rcs, cf_kflag);
599 	}
600 
601 	rcs_write(cf->file_rcs);
602 
603 	if (cf->file_status == FILE_REMOVED) {
604 		strlcpy(nbuf, "Removed", sizeof(nbuf));
605 	} else if (cf->file_status == FILE_ADDED) {
606 		if (cf->file_rcs->rf_dead == 1)
607 			strlcpy(nbuf, "Initial Revision", sizeof(nbuf));
608 		else
609 			rcsnum_tostr(nrev, nbuf, sizeof(nbuf));
610 	} else if (cf->file_status == FILE_MODIFIED) {
611 		rcsnum_tostr(nrev, nbuf, sizeof(nbuf));
612 	}
613 
614 	if (verbosity > 1)
615 		cvs_printf("new revision: %s\n", nbuf);
616 
617 	(void)unlink(cf->file_path);
618 	(void)close(cf->fd);
619 	cf->fd = -1;
620 
621 	if (cf->file_status != FILE_REMOVED) {
622 		cvs_checkout_file(cf, nrev, NULL, CO_COMMIT);
623 	} else {
624 		entlist = cvs_ent_open(cf->file_wd);
625 		cvs_ent_remove(entlist, cf->file_name);
626 
627 		cvs_get_repository_path(cf->file_wd, repo, PATH_MAX);
628 
629 		(void)xsnprintf(attic, PATH_MAX, "%s/%s",
630 		    repo, CVS_PATH_ATTIC);
631 
632 		if (mkdir(attic, 0755) == -1 && errno != EEXIST)
633 			fatal("cvs_commit_local: failed to create Attic");
634 
635 		(void)xsnprintf(attic, PATH_MAX, "%s/%s/%s%s", repo,
636 		    CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT);
637 
638 		if (rename(cf->file_rpath, attic) == -1)
639 			fatal("cvs_commit_local: failed to move %s to Attic",
640 			    cf->file_path);
641 
642 		if (cvs_server_active == 1)
643 			cvs_server_update_entry("Remove-entry", cf);
644 	}
645 
646 	if (verbosity > 1)
647 		cvs_printf("done\n");
648 	else {
649 		cvs_log(LP_NOTICE, "checking in '%s'; revision %s -> %s",
650 		    cf->file_path, rbuf, nbuf);
651 	}
652 
653 	if (line_list != NULL) {
654 		fi = xcalloc(1, sizeof(*fi));
655 		fi->file_path = xstrdup(cf->file_path);
656 		fi->crevstr = xstrdup(rbuf);
657 		fi->nrevstr = xstrdup(nbuf);
658 		if (tag != NULL)
659 			fi->tag_new = xstrdup(tag);
660 		TAILQ_INSERT_TAIL(&files_info, fi, flist);
661 	}
662 
663 	switch (cf->file_status) {
664 	case FILE_MODIFIED:
665 		histtype = CVS_HISTORY_COMMIT_MODIFIED;
666 		break;
667 	case FILE_ADDED:
668 		histtype = CVS_HISTORY_COMMIT_ADDED;
669 		break;
670 	case FILE_REMOVED:
671 		histtype = CVS_HISTORY_COMMIT_REMOVED;
672 		break;
673 	default:
674 		histtype = -1;
675 		break;
676 	}
677 
678 	free(crev);
679 
680 	if (histtype != -1)
681 		cvs_history_add(histtype, cf, NULL);
682 	else
683 		cvs_log(LP_NOTICE, "histtype was -1 for %s", cf->file_path);
684 }
685 
686 static BUF *
commit_diff(struct cvs_file * cf,RCSNUM * rev,int reverse)687 commit_diff(struct cvs_file *cf, RCSNUM *rev, int reverse)
688 {
689 	int fd1, fd2, d;
690 	char *p1, *p2;
691 	BUF *b;
692 
693 	(void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
694 
695 	if (cf->file_status == FILE_MODIFIED ||
696 	    cf->file_status == FILE_ADDED) {
697 		b = buf_load_fd(cf->fd);
698 		fd1 = buf_write_stmp(b, p1, NULL);
699 		buf_free(b);
700 	} else {
701 		fd1 = rcs_rev_write_stmp(cf->file_rcs, rev, p1, 0);
702 	}
703 
704 	(void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
705 	fd2 = rcs_rev_write_stmp(cf->file_rcs, rev, p2, RCS_KWEXP_NONE);
706 
707 	b = buf_alloc(128);
708 
709 	diff_format = D_RCSDIFF;
710 
711 	if (reverse == 1)
712 		d = diffreg(p2, p1, fd2, fd1, b, D_FORCEASCII);
713 	else
714 		d = diffreg(p1, p2, fd1, fd2, b, D_FORCEASCII);
715 	if (d == D_ERROR)
716 		fatal("commit_diff: failed to get RCS patch");
717 
718 	close(fd1);
719 	close(fd2);
720 
721 	free(p1);
722 	free(p2);
723 
724 	return (b);
725 }
726 
727 static void
commit_desc_set(struct cvs_file * cf)728 commit_desc_set(struct cvs_file *cf)
729 {
730 	BUF *bp;
731 	int fd;
732 	char desc_path[PATH_MAX], *desc;
733 
734 	(void)xsnprintf(desc_path, PATH_MAX, "%s/%s/%s%s",
735 	    cf->file_wd, CVS_PATH_CVSDIR, cf->file_name, CVS_DESCR_FILE_EXT);
736 
737 	if ((fd = open(desc_path, O_RDONLY)) == -1)
738 		return;
739 
740 	bp = buf_load_fd(fd);
741 	buf_putc(bp, '\0');
742 	desc = buf_release(bp);
743 
744 	rcs_desc_set(cf->file_rcs, desc);
745 
746 	(void)close(fd);
747 	(void)cvs_unlink(desc_path);
748 
749 	free(desc);
750 }
751