1*c638a04bSjoris /* $OpenBSD: diff.c,v 1.140 2008/06/11 20:55:34 joris Exp $ */ 208f90673Sjfb /* 3fd687d09Stobias * Copyright (c) 2008 Tobias Stoeckmann <tobias@openbsd.org> 43ad3fb45Sjoris * Copyright (c) 2006 Joris Vink <joris@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> 20fd660bf2Stobias #include <sys/time.h> 211f8531bdSotto 221f8531bdSotto #include <errno.h> 232e0d696aSjoris #include <fcntl.h> 24fd660bf2Stobias #include <stdlib.h> 251f8531bdSotto #include <string.h> 266534056aStobias #include <time.h> 271f8531bdSotto #include <unistd.h> 2808f90673Sjfb 2908f90673Sjfb #include "cvs.h" 30af5bb824Sniallo #include "diff.h" 319fac60a5Sjoris #include "remote.h" 3208f90673Sjfb 333ad3fb45Sjoris void cvs_diff_local(struct cvs_file *); 34ec6ed1abSjoris 354230e8b3Sjoris static int Nflag = 0; 36fd660bf2Stobias static int force_head = 0; 3737fdff3fStobias static char *koptstr; 384230e8b3Sjoris static char *rev1 = NULL; 394230e8b3Sjoris static char *rev2 = NULL; 40be756b91Stobias static time_t date1 = -1; 41be756b91Stobias static time_t date2 = -1; 4267caf486Sjoris 43e4276007Sjfb struct cvs_cmd cvs_cmd_diff = { 44f331ff59Stobias CVS_OP_DIFF, CVS_USE_WDIR, "diff", 45e4276007Sjfb { "di", "dif" }, 46e4276007Sjfb "Show differences between revisions", 47bcf22459Stobias "[-cilNnpRu] [[-D date] [-r rev] [-D date2 | -r rev2]] " 48c9150269Sxsa "[-k mode] [file ...]", 4937fdff3fStobias "cfD:ik:lNnpr:Ru", 50fd660bf2Stobias NULL, 51fd660bf2Stobias cvs_diff 52fd660bf2Stobias }; 53fd660bf2Stobias 54fd660bf2Stobias struct cvs_cmd cvs_cmd_rdiff = { 55fd660bf2Stobias CVS_OP_RDIFF, 0, "rdiff", 56fd660bf2Stobias { "patch", "pa" }, 57fd660bf2Stobias "Show differences between revisions", 58fd660bf2Stobias "[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev\n" 59fd660bf2Stobias "[-D date2 | -r rev2] [-k mode] module ...", 6037fdff3fStobias "cfD:k:lr:RuV:", 6116cfc147Sjoris NULL, 623ad3fb45Sjoris cvs_diff 63e4276007Sjfb }; 64e4276007Sjfb 653ad3fb45Sjoris int 663ad3fb45Sjoris cvs_diff(int argc, char **argv) 6708f90673Sjfb { 68e9d83458Stobias int ch, flags; 693ad3fb45Sjoris char *arg = "."; 703ad3fb45Sjoris struct cvs_recursion cr; 7108f90673Sjfb 721890abdaSjoris flags = CR_RECURSE_DIRS; 73bc6ad1fbStobias strlcpy(diffargs, cvs_cmdop == CVS_OP_DIFF ? "diff" : "rdiff", 74bc6ad1fbStobias sizeof(diffargs)); 75dc6a6879Sjfb 76e9d83458Stobias while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_DIFF ? 77e9d83458Stobias cvs_cmd_diff.cmd_opts : cvs_cmd_rdiff.cmd_opts)) != -1) { 7808f90673Sjfb switch (ch) { 7908f90673Sjfb case 'c': 80f5638424Sjfb strlcat(diffargs, " -c", sizeof(diffargs)); 81f9b67873Sniallo diff_format = D_CONTEXT; 8208f90673Sjfb break; 83be756b91Stobias case 'D': 84be756b91Stobias if (date1 == -1 && rev1 == NULL) { 85be756b91Stobias date1 = cvs_date_parse(optarg); 86be756b91Stobias } else if (date2 == -1 && rev2 == NULL) { 87be756b91Stobias date2 = cvs_date_parse(optarg); 88be756b91Stobias } else { 89be756b91Stobias fatal("no more than 2 revisions/dates can" 90be756b91Stobias " be specified"); 91be756b91Stobias } 92be756b91Stobias break; 93fd660bf2Stobias case 'f': 94fd660bf2Stobias force_head = 1; 95fd660bf2Stobias break; 96fd660bf2Stobias case 'i': 97fd660bf2Stobias strlcat(diffargs, " -i", sizeof(diffargs)); 98fd660bf2Stobias diff_iflag = 1; 99fd660bf2Stobias break; 10037fdff3fStobias case 'k': 10137fdff3fStobias koptstr = optarg; 10237fdff3fStobias kflag = rcs_kflag_get(koptstr); 10337fdff3fStobias if (RCS_KWEXP_INVAL(kflag)) { 10437fdff3fStobias cvs_log(LP_ERR, 1050bc1d395Stobias "invalid RCS keyword expansion mode"); 106aeaaf4a6Stobias fatal("%s", cvs_cmdop == CVS_OP_DIFF ? 107aeaaf4a6Stobias cvs_cmd_diff.cmd_synopsis : 108aeaaf4a6Stobias cvs_cmd_rdiff.cmd_synopsis); 10937fdff3fStobias } 11037fdff3fStobias break; 1111890abdaSjoris case 'l': 1121890abdaSjoris flags &= ~CR_RECURSE_DIRS; 1131890abdaSjoris break; 114394180a4Sjfb case 'n': 115394180a4Sjfb strlcat(diffargs, " -n", sizeof(diffargs)); 116f9b67873Sniallo diff_format = D_RCSDIFF; 117394180a4Sjfb break; 11867caf486Sjoris case 'N': 119fd687d09Stobias strlcat(diffargs, " -N", sizeof(diffargs)); 1202d6f447eSjoris Nflag = 1; 12167caf486Sjoris break; 122261cb0daSjoris case 'p': 123261cb0daSjoris strlcat(diffargs, " -p", sizeof(diffargs)); 124261cb0daSjoris diff_pflag = 1; 125261cb0daSjoris break; 126bcf22459Stobias case 'R': 127bcf22459Stobias flags |= CR_RECURSE_DIRS; 128bcf22459Stobias break; 12908f90673Sjfb case 'r': 130be756b91Stobias if (date1 == -1 && rev1 == NULL) { 1314230e8b3Sjoris rev1 = optarg; 132be756b91Stobias } else if (date2 == -1 && rev2 == NULL) { 1334230e8b3Sjoris rev2 = optarg; 13416cfc147Sjoris } else { 1353ad3fb45Sjoris fatal("no more than 2 revisions/dates can" 13608f90673Sjfb " be specified"); 13708f90673Sjfb } 13808f90673Sjfb break; 13908f90673Sjfb case 'u': 140f5638424Sjfb strlcat(diffargs, " -u", sizeof(diffargs)); 141f9b67873Sniallo diff_format = D_UNIFIED; 14208f90673Sjfb break; 14337fdff3fStobias case 'V': 14437fdff3fStobias fatal("the -V option is obsolete " 14537fdff3fStobias "and should not be used"); 14608f90673Sjfb default: 147aeaaf4a6Stobias fatal("%s", cvs_cmdop == CVS_OP_DIFF ? 148aeaaf4a6Stobias cvs_cmd_diff.cmd_synopsis : 149aeaaf4a6Stobias cvs_cmd_rdiff.cmd_synopsis); 15008f90673Sjfb } 15108f90673Sjfb } 15208f90673Sjfb 1533ad3fb45Sjoris argc -= optind; 1543ad3fb45Sjoris argv += optind; 155dc6a6879Sjfb 1563ad3fb45Sjoris cr.enterdir = NULL; 1573ad3fb45Sjoris cr.leavedir = NULL; 1589fac60a5Sjoris 159fd660bf2Stobias if (cvs_cmdop == CVS_OP_RDIFF) { 160fd660bf2Stobias if (rev1 == NULL) 161fd660bf2Stobias fatal("must specify at least one revision/date!"); 162fd660bf2Stobias 163fd687d09Stobias if (!argc) 164fd687d09Stobias fatal("%s", cvs_cmd_rdiff.cmd_synopsis); 165fd687d09Stobias 166fd660bf2Stobias if (!diff_format) { 167fd660bf2Stobias strlcat(diffargs, " -c", sizeof(diffargs)); 168fd660bf2Stobias diff_format = D_CONTEXT; 169fd660bf2Stobias } 170fd660bf2Stobias 171fd660bf2Stobias flags |= CR_REPO; 172fd660bf2Stobias } 173fd660bf2Stobias 1749fac60a5Sjoris if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 17580f6ca9bSjoris cvs_client_connect_to_server(); 1769fac60a5Sjoris cr.fileproc = cvs_client_sendfile; 1779fac60a5Sjoris 1789fac60a5Sjoris if (!(flags & CR_RECURSE_DIRS)) 1799fac60a5Sjoris cvs_client_send_request("Argument -l"); 1809fac60a5Sjoris 18137fdff3fStobias if (kflag) 18237fdff3fStobias cvs_client_send_request("Argument -k%s", koptstr); 18337fdff3fStobias 1849fac60a5Sjoris switch (diff_format) { 1859fac60a5Sjoris case D_CONTEXT: 1869fac60a5Sjoris cvs_client_send_request("Argument -c"); 1879fac60a5Sjoris break; 1889fac60a5Sjoris case D_RCSDIFF: 1899fac60a5Sjoris cvs_client_send_request("Argument -n"); 1909fac60a5Sjoris break; 1919fac60a5Sjoris case D_UNIFIED: 1929fac60a5Sjoris cvs_client_send_request("Argument -u"); 1939fac60a5Sjoris break; 1949fac60a5Sjoris default: 1959fac60a5Sjoris break; 1969fac60a5Sjoris } 1979fac60a5Sjoris 1989fac60a5Sjoris if (Nflag == 1) 1999fac60a5Sjoris cvs_client_send_request("Argument -N"); 2009fac60a5Sjoris 2019fac60a5Sjoris if (diff_pflag == 1) 2029fac60a5Sjoris cvs_client_send_request("Argument -p"); 2039fac60a5Sjoris 2049fac60a5Sjoris if (rev1 != NULL) 2059fac60a5Sjoris cvs_client_send_request("Argument -r%s", rev1); 2069fac60a5Sjoris if (rev2 != NULL) 2079fac60a5Sjoris cvs_client_send_request("Argument -r%s", rev2); 2089fac60a5Sjoris } else { 209fd660bf2Stobias if (cvs_cmdop == CVS_OP_RDIFF && 210fd660bf2Stobias chdir(current_cvsroot->cr_dir) == -1) 211fd660bf2Stobias fatal("cvs_diff: %s", strerror(errno)); 212fd660bf2Stobias 213bc5d89feSjoris cr.fileproc = cvs_diff_local; 2149fac60a5Sjoris } 2159fac60a5Sjoris 2161890abdaSjoris cr.flags = flags; 217dc6a6879Sjfb 2184230e8b3Sjoris diff_rev1 = diff_rev2 = NULL; 2194230e8b3Sjoris 220fd660bf2Stobias if (cvs_cmdop == CVS_OP_DIFF || 221fd660bf2Stobias current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 2223ad3fb45Sjoris if (argc > 0) 2233ad3fb45Sjoris cvs_file_run(argc, argv, &cr); 224d990145dSray else 2253ad3fb45Sjoris cvs_file_run(1, &arg, &cr); 226fd660bf2Stobias } 227e4276007Sjfb 2289fac60a5Sjoris if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 2299fac60a5Sjoris cvs_client_send_files(argv, argc); 2309fac60a5Sjoris cvs_client_senddir("."); 231fd660bf2Stobias 232fd660bf2Stobias cvs_client_send_request((cvs_cmdop == CVS_OP_RDIFF) ? 233fd660bf2Stobias "rdiff" : "diff"); 234fd660bf2Stobias 2359fac60a5Sjoris cvs_client_get_responses(); 2369fac60a5Sjoris } 2379fac60a5Sjoris 238e4276007Sjfb return (0); 239e4276007Sjfb } 240e4276007Sjfb 24101af718aSjoris void 2423ad3fb45Sjoris cvs_diff_local(struct cvs_file *cf) 243f9b67873Sniallo { 244741443cbSjoris BUF *b1; 2452e0d696aSjoris int fd1, fd2; 2463ad3fb45Sjoris struct stat st; 2473ad3fb45Sjoris struct timeval tv[2], tv2[2]; 2486534056aStobias struct tm datetm; 249be756b91Stobias char rbuf[CVS_REV_BUFSZ], tbuf[CVS_TIME_BUFSZ], *p1, *p2; 250d71a0c9bSxsa 251741443cbSjoris b1 = NULL; 252fd687d09Stobias fd1 = fd2 = -1; 253fd687d09Stobias p1 = p2 = NULL; 254f9b67873Sniallo 2553ad3fb45Sjoris cvs_log(LP_TRACE, "cvs_diff_local(%s)", cf->file_path); 2563ad3fb45Sjoris 2573ad3fb45Sjoris if (cf->file_type == CVS_DIR) { 2583ad3fb45Sjoris if (verbosity > 1) 2593ad3fb45Sjoris cvs_log(LP_NOTICE, "Diffing inside %s", cf->file_path); 2603ad3fb45Sjoris return; 2613ad3fb45Sjoris } 2623ad3fb45Sjoris 26351ef6581Sjoris cvs_file_classify(cf, cvs_directory_tag); 2643ad3fb45Sjoris 265fd687d09Stobias if (cvs_cmdop == CVS_OP_DIFF) { 266fd687d09Stobias if (cf->file_ent == NULL) { 267fd687d09Stobias cvs_log(LP_ERR, "I know nothing about %s", 26867caf486Sjoris cf->file_path); 2693ad3fb45Sjoris return; 27067caf486Sjoris } 2713ad3fb45Sjoris 272fd687d09Stobias switch (cf->file_ent->ce_status) { 273fd687d09Stobias case CVS_ENT_ADDED: 274fd687d09Stobias if (Nflag == 0) { 275fd687d09Stobias cvs_log(LP_ERR, "%s is a new entry, no " 276fd687d09Stobias "comparison available", cf->file_path); 277fd687d09Stobias return; 278fd687d09Stobias } 279fd687d09Stobias if (cf->fd == -1) { 28010e77a1eStobias if (!cvs_server_active) 281fd687d09Stobias cvs_log(LP_ERR, "cannot find %s", 282fd687d09Stobias cf->file_path); 283fd687d09Stobias return; 284fd687d09Stobias } 285fd687d09Stobias break; 286fd687d09Stobias case CVS_ENT_REMOVED: 287fd687d09Stobias if (Nflag == 0) { 288fd687d09Stobias cvs_log(LP_ERR, "%s was removed, no " 289fd687d09Stobias "comparison available", cf->file_path); 290fd687d09Stobias return; 291fd687d09Stobias } 292fd687d09Stobias if (cf->file_rcs == NULL) { 293fd687d09Stobias cvs_log(LP_ERR, "cannot find RCS file for %s", 294fd687d09Stobias cf->file_path); 295fd687d09Stobias return; 296fd687d09Stobias } 297fd687d09Stobias break; 298fd687d09Stobias default: 299*c638a04bSjoris if (cvs_server_active != 1 && cf->fd == -1) { 300fd687d09Stobias cvs_log(LP_ERR, "cannot find %s", 301fd687d09Stobias cf->file_path); 302fd687d09Stobias return; 303fd687d09Stobias } 304*c638a04bSjoris 305fd687d09Stobias if (cf->file_rcs == NULL) { 306fd687d09Stobias cvs_log(LP_ERR, "cannot find RCS file for %s", 307fd687d09Stobias cf->file_path); 308fd687d09Stobias return; 309fd687d09Stobias } 310fd687d09Stobias break; 311fd687d09Stobias } 312fd687d09Stobias } 313fd687d09Stobias 314be756b91Stobias if (cf->file_status == FILE_UPTODATE && rev1 == NULL && rev2 == NULL && 315be756b91Stobias date1 == -1 && date2 == -1) 316fd687d09Stobias return; 317fd687d09Stobias 318fd687d09Stobias if (kflag && cf->file_rcs != NULL) 31937fdff3fStobias rcs_kwexp_set(cf->file_rcs, kflag); 32037fdff3fStobias 321fd687d09Stobias if (cf->file_rcs == NULL) 322fd687d09Stobias diff_rev1 = NULL; 323be756b91Stobias else if (rev1 != NULL || date1 != -1) { 324be756b91Stobias cvs_specified_date = date1; 325fd687d09Stobias diff_rev1 = rcs_translate_tag(rev1, cf->file_rcs); 326fd687d09Stobias if (diff_rev1 == NULL && cvs_cmdop == CVS_OP_DIFF) { 327be756b91Stobias if (rev1 != NULL) 328fd687d09Stobias cvs_log(LP_ERR, "tag %s not in file %s", rev1, 329fd687d09Stobias cf->file_path); 330be756b91Stobias else { 3316534056aStobias gmtime_r(&cvs_specified_date, &datetm); 332be756b91Stobias strftime(tbuf, sizeof(tbuf), 3336534056aStobias "%Y.%m.%d.%H.%M.%S", &datetm); 334be756b91Stobias cvs_log(LP_ERR, "no revision for date %s in " 335be756b91Stobias "file %s", tbuf, cf->file_path); 336be756b91Stobias } 337fd687d09Stobias goto cleanup; 338fd687d09Stobias } else if (diff_rev1 == NULL && cvs_cmdop == CVS_OP_RDIFF && 339fd687d09Stobias force_head) { 340fd660bf2Stobias /* -f is not allowed for unknown symbols */ 341fd687d09Stobias if ((diff_rev1 = rcsnum_parse(rev1)) == NULL) 342fd660bf2Stobias fatal("no such tag %s", rev1); 343fd660bf2Stobias rcsnum_free(diff_rev1); 344fd660bf2Stobias 345fd687d09Stobias diff_rev1 = cf->file_rcs->rf_head; 346fd660bf2Stobias } 347be756b91Stobias cvs_specified_date = -1; 348fd687d09Stobias } else if (cvs_cmdop == CVS_OP_DIFF) { 349fd687d09Stobias if (cf->file_ent->ce_status == CVS_ENT_ADDED) 350fd687d09Stobias diff_rev1 = NULL; 351fd687d09Stobias else 352fd687d09Stobias diff_rev1 = cf->file_ent->ce_rev; 353fd660bf2Stobias } 35451ef6581Sjoris 355fd687d09Stobias if (cf->file_rcs == NULL) 356fd687d09Stobias diff_rev2 = NULL; 357be756b91Stobias else if (rev2 != NULL || date2 != -1) { 358be756b91Stobias cvs_specified_date = date2; 359fd687d09Stobias diff_rev2 = rcs_translate_tag(rev2, cf->file_rcs); 360fd687d09Stobias if (diff_rev2 == NULL && cvs_cmdop == CVS_OP_DIFF) { 361be756b91Stobias if (rev2 != NULL) { 362fd687d09Stobias cvs_log(LP_ERR, "tag %s not in file %s", rev2, 363fd687d09Stobias cf->file_path); 364be756b91Stobias } else { 3656534056aStobias gmtime_r(&cvs_specified_date, &datetm); 366be756b91Stobias strftime(tbuf, sizeof(tbuf), 3676534056aStobias "%Y.%m.%d.%H.%M.%S", &datetm); 368be756b91Stobias cvs_log(LP_ERR, "no revision for date %s in " 369be756b91Stobias "file %s", tbuf, cf->file_path); 370be756b91Stobias } 371fd687d09Stobias goto cleanup; 372fd687d09Stobias } else if (diff_rev2 == NULL && cvs_cmdop == CVS_OP_RDIFF && 373fd687d09Stobias force_head) { 374fd660bf2Stobias /* -f is not allowed for unknown symbols */ 375fd687d09Stobias if ((diff_rev2 = rcsnum_parse(rev2)) == NULL) 376fd660bf2Stobias fatal("no such tag %s", rev2); 377fd660bf2Stobias rcsnum_free(diff_rev2); 378fd660bf2Stobias 379fd687d09Stobias diff_rev2 = cf->file_rcs->rf_head; 380fd660bf2Stobias } 381be756b91Stobias cvs_specified_date = -1; 382fd687d09Stobias } else if (cvs_cmdop == CVS_OP_RDIFF) 383fd687d09Stobias diff_rev2 = cf->file_rcs->rf_head; 384fd687d09Stobias else if (cf->file_ent->ce_status == CVS_ENT_REMOVED) 385fd687d09Stobias diff_rev2 = NULL; 386fd687d09Stobias 387fd687d09Stobias if (diff_rev1 != NULL && diff_rev2 != NULL && 388fd687d09Stobias rcsnum_cmp(diff_rev1, diff_rev2, 0) == 0) 389fd687d09Stobias goto cleanup; 390fd687d09Stobias 391fd687d09Stobias switch (cvs_cmdop) { 392fd687d09Stobias case CVS_OP_DIFF: 393fd687d09Stobias if (cf->file_status == FILE_UPTODATE && 394fd687d09Stobias rcsnum_cmp(diff_rev1, cf->file_rcsrev, 0) == 0) 395fd687d09Stobias goto cleanup; 396fd687d09Stobias break; 397fd687d09Stobias case CVS_OP_RDIFF: 398fd687d09Stobias if (diff_rev1 == NULL && diff_rev2 == NULL) 399fd687d09Stobias goto cleanup; 400fd687d09Stobias break; 401fd660bf2Stobias } 402fd660bf2Stobias 403fd660bf2Stobias cvs_printf("Index: %s\n", cf->file_path); 404fd660bf2Stobias if (cvs_cmdop == CVS_OP_DIFF) 405fd687d09Stobias cvs_printf("%s\nRCS file: %s\n", RCS_DIFF_DIV, 406fd687d09Stobias cf->file_rcs != NULL ? cf->file_rpath : cf->file_path); 407fd660bf2Stobias 408fd660bf2Stobias if (diff_rev1 != NULL) { 409fd687d09Stobias if (cvs_cmdop == CVS_OP_DIFF && diff_rev1 != NULL) { 410fd687d09Stobias (void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf)); 411fd687d09Stobias cvs_printf("retrieving revision %s\n", rbuf); 412fd687d09Stobias } 4133ad3fb45Sjoris 414fd687d09Stobias tv[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev1); 4153ad3fb45Sjoris tv[0].tv_usec = 0; 4163ad3fb45Sjoris tv[1] = tv[0]; 417741443cbSjoris 418fd687d09Stobias (void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); 419fd687d09Stobias fd1 = rcs_rev_write_stmp(cf->file_rcs, diff_rev1, p1, 0); 4202e0d696aSjoris if (futimes(fd1, tv) == -1) 421fd660bf2Stobias fatal("cvs_diff_local: utimes failed"); 422fd660bf2Stobias } 4233ad3fb45Sjoris 424fd687d09Stobias if (diff_rev2 != NULL) { 425fd687d09Stobias if (cvs_cmdop == CVS_OP_DIFF && rev2 != NULL) { 426fd660bf2Stobias (void)rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf)); 427fd687d09Stobias cvs_printf("retrieving revision %s\n", rbuf); 428fd687d09Stobias } 4293ad3fb45Sjoris 4303ad3fb45Sjoris tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev2); 4313ad3fb45Sjoris tv2[0].tv_usec = 0; 4323ad3fb45Sjoris tv2[1] = tv2[0]; 433741443cbSjoris 434fd687d09Stobias (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 4352e0d696aSjoris fd2 = rcs_rev_write_stmp(cf->file_rcs, diff_rev2, p2, 0); 4362e0d696aSjoris if (futimes(fd2, tv2) == -1) 437fd660bf2Stobias fatal("cvs_diff_local: utimes failed"); 438fd687d09Stobias } else if (cvs_cmdop == CVS_OP_DIFF && cf->fd != -1 && 439fd687d09Stobias cf->file_ent->ce_status != CVS_ENT_REMOVED) { 4403ad3fb45Sjoris if (fstat(cf->fd, &st) == -1) 4413ad3fb45Sjoris fatal("fstat failed %s", strerror(errno)); 4429aad96bcStobias b1 = cvs_buf_load_fd(cf->fd); 4433ad3fb45Sjoris 4443ad3fb45Sjoris tv2[0].tv_sec = st.st_mtime; 4453ad3fb45Sjoris tv2[0].tv_usec = 0; 4463ad3fb45Sjoris tv2[1] = tv2[0]; 447741443cbSjoris 448fd687d09Stobias (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 4492e0d696aSjoris fd2 = cvs_buf_write_stmp(b1, p2, tv2); 450741443cbSjoris cvs_buf_free(b1); 4513ad3fb45Sjoris } 4523ad3fb45Sjoris 453fd687d09Stobias switch (cvs_cmdop) { 454fd687d09Stobias case CVS_OP_DIFF: 4553ad3fb45Sjoris cvs_printf("%s", diffargs); 4563ad3fb45Sjoris 457fd687d09Stobias if (rev1 != NULL && diff_rev1 != NULL) { 458fd687d09Stobias (void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf)); 4593ad3fb45Sjoris cvs_printf(" -r%s", rbuf); 4603ad3fb45Sjoris 461fd687d09Stobias if (rev2 != NULL && diff_rev2 != NULL) { 462fd660bf2Stobias (void)rcsnum_tostr(diff_rev2, rbuf, 463fd660bf2Stobias sizeof(rbuf)); 4643ad3fb45Sjoris cvs_printf(" -r%s", rbuf); 4653ad3fb45Sjoris } 46667caf486Sjoris } 4673ad3fb45Sjoris 468fd660bf2Stobias if (diff_rev2 == NULL) 469fd687d09Stobias cvs_printf(" %s", cf->file_path); 47009d59ca3Stobias cvs_printf("\n"); 471fd687d09Stobias break; 472fd687d09Stobias case CVS_OP_RDIFF: 473fd660bf2Stobias cvs_printf("diff "); 474fd660bf2Stobias switch (diff_format) { 475fd660bf2Stobias case D_CONTEXT: 476fd660bf2Stobias cvs_printf("-c "); 477fd660bf2Stobias break; 478fd660bf2Stobias case D_RCSDIFF: 479fd660bf2Stobias cvs_printf("-n "); 480fd660bf2Stobias break; 481fd660bf2Stobias case D_UNIFIED: 482fd660bf2Stobias cvs_printf("-u "); 483fd660bf2Stobias break; 484fd660bf2Stobias default: 485fd660bf2Stobias break; 486fd660bf2Stobias } 487fd660bf2Stobias if (diff_rev1 == NULL) { 488fd660bf2Stobias cvs_printf("%s ", CVS_PATH_DEVNULL); 489fd660bf2Stobias } else { 490fd660bf2Stobias (void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf)); 491fd660bf2Stobias cvs_printf("%s:%s ", cf->file_path, rbuf); 492fd660bf2Stobias } 4933ad3fb45Sjoris 494fd660bf2Stobias if (diff_rev2 == NULL) { 495fd660bf2Stobias cvs_printf("%s:removed\n", cf->file_path); 496fd660bf2Stobias } else { 497fd660bf2Stobias (void)rcsnum_tostr(diff_rev2 != NULL ? diff_rev2 : 498fd660bf2Stobias cf->file_rcs->rf_head, rbuf, sizeof(rbuf)); 499fd660bf2Stobias cvs_printf("%s:%s\n", cf->file_path, rbuf); 500fd660bf2Stobias } 501fd687d09Stobias break; 502fd660bf2Stobias } 503fd660bf2Stobias 504fd687d09Stobias if (fd1 == -1) { 505fd687d09Stobias if ((fd1 = open(CVS_PATH_DEVNULL, O_RDONLY, 0)) == -1) 506fd687d09Stobias fatal("cannot open %s", CVS_PATH_DEVNULL); 507fd687d09Stobias } 508fd687d09Stobias if (fd2 == -1) { 509fd687d09Stobias if ((fd2 = open(CVS_PATH_DEVNULL, O_RDONLY, 0)) == -1) 510fd687d09Stobias fatal("cannot open %s", CVS_PATH_DEVNULL); 511741443cbSjoris } 5123ad3fb45Sjoris 513fd687d09Stobias if (cvs_diffreg(p1 != NULL ? cf->file_path : CVS_PATH_DEVNULL, 514fd687d09Stobias p2 != NULL ? cf->file_path : CVS_PATH_DEVNULL, fd1, fd2, NULL) 515fd687d09Stobias == D_ERROR) 516d71a0c9bSxsa fatal("cvs_diff_local: failed to get RCS patch"); 517d71a0c9bSxsa 5182e0d696aSjoris close(fd1); 5192e0d696aSjoris close(fd2); 5202e0d696aSjoris 5213ad3fb45Sjoris cvs_worklist_run(&temp_files, cvs_worklist_unlink); 52267caf486Sjoris 523d71a0c9bSxsa if (p1 != NULL) 524d71a0c9bSxsa xfree(p1); 525d71a0c9bSxsa if (p2 != NULL) 526d71a0c9bSxsa xfree(p2); 527d71a0c9bSxsa 528fd687d09Stobias cleanup: 529fd687d09Stobias if (diff_rev1 != NULL && 530fd687d09Stobias (cf->file_rcs == NULL || diff_rev1 != cf->file_rcs->rf_head) && 531fd687d09Stobias (cf->file_ent == NULL || diff_rev1 != cf->file_ent->ce_rev)) 532fd687d09Stobias xfree(diff_rev1); 533fd687d09Stobias diff_rev1 = NULL; 534261cb0daSjoris 535fd687d09Stobias if (diff_rev2 != NULL && 536fd687d09Stobias (cf->file_rcs == NULL || diff_rev2 != cf->file_rcs->rf_head)) 537fd687d09Stobias xfree(diff_rev2); 538fd687d09Stobias diff_rev2 = NULL; 539f9b67873Sniallo } 540