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