1*092db204Sray /* $OpenBSD: diff.c,v 1.159 2010/07/30 21:47:18 ray 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 35219c50abSray static int dflags = 0; 364230e8b3Sjoris static int Nflag = 0; 37fd660bf2Stobias static int force_head = 0; 3837fdff3fStobias static char *koptstr; 394230e8b3Sjoris static char *rev1 = NULL; 404230e8b3Sjoris static char *rev2 = NULL; 41be756b91Stobias static time_t date1 = -1; 42be756b91Stobias static time_t date2 = -1; 43909bf3e0Stobias static char *dateflag1 = NULL; 44909bf3e0Stobias static char *dateflag2 = NULL; 4567caf486Sjoris 46e4276007Sjfb struct cvs_cmd cvs_cmd_diff = { 47f331ff59Stobias CVS_OP_DIFF, CVS_USE_WDIR, "diff", 48e4276007Sjfb { "di", "dif" }, 49e4276007Sjfb "Show differences between revisions", 506412cad2Ssthen "[-abcdilNnpRuw] [[-D date] [-r rev] [-D date2 | -r rev2]] " 51c9150269Sxsa "[-k mode] [file ...]", 5272026f1aSnicm "abcfC:dD:ik:lNnpr:RuU:w", 53fd660bf2Stobias NULL, 54fd660bf2Stobias cvs_diff 55fd660bf2Stobias }; 56fd660bf2Stobias 57fd660bf2Stobias struct cvs_cmd cvs_cmd_rdiff = { 58fd660bf2Stobias CVS_OP_RDIFF, 0, "rdiff", 59fd660bf2Stobias { "patch", "pa" }, 60fd660bf2Stobias "Show differences between revisions", 61fd660bf2Stobias "[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev\n" 62fd660bf2Stobias "[-D date2 | -r rev2] [-k mode] module ...", 6337fdff3fStobias "cfD:k:lr:RuV:", 6416cfc147Sjoris NULL, 653ad3fb45Sjoris cvs_diff 66e4276007Sjfb }; 67e4276007Sjfb 683ad3fb45Sjoris int 693ad3fb45Sjoris cvs_diff(int argc, char **argv) 7008f90673Sjfb { 71e9d83458Stobias int ch, flags; 723ad3fb45Sjoris char *arg = "."; 7372026f1aSnicm const char *errstr; 743ad3fb45Sjoris struct cvs_recursion cr; 7508f90673Sjfb 761890abdaSjoris flags = CR_RECURSE_DIRS; 77bc6ad1fbStobias strlcpy(diffargs, cvs_cmdop == CVS_OP_DIFF ? "diff" : "rdiff", 78bc6ad1fbStobias sizeof(diffargs)); 79dc6a6879Sjfb 80e9d83458Stobias while ((ch = getopt(argc, argv, cvs_cmdop == CVS_OP_DIFF ? 81e9d83458Stobias cvs_cmd_diff.cmd_opts : cvs_cmd_rdiff.cmd_opts)) != -1) { 8208f90673Sjfb switch (ch) { 836412cad2Ssthen case 'a': 846412cad2Ssthen strlcat(diffargs, " -a", sizeof(diffargs)); 85219c50abSray dflags |= D_FORCEASCII; 866412cad2Ssthen break; 876412cad2Ssthen case 'b': 886412cad2Ssthen strlcat(diffargs, " -b", sizeof(diffargs)); 89219c50abSray dflags |= D_FOLDBLANKS; 906412cad2Ssthen break; 9108f90673Sjfb case 'c': 92f5638424Sjfb strlcat(diffargs, " -c", sizeof(diffargs)); 93f9b67873Sniallo diff_format = D_CONTEXT; 9408f90673Sjfb break; 9572026f1aSnicm case 'C': 9672026f1aSnicm diff_context = strtonum(optarg, 0, INT_MAX, &errstr); 9772026f1aSnicm if (errstr != NULL) 9872026f1aSnicm fatal("context lines %s: %s", errstr, optarg); 9972026f1aSnicm strlcat(diffargs, " -C ", sizeof(diffargs)); 10072026f1aSnicm strlcat(diffargs, optarg, sizeof(diffargs)); 10172026f1aSnicm diff_format = D_CONTEXT; 10272026f1aSnicm break; 1036412cad2Ssthen case 'd': 1046412cad2Ssthen strlcat(diffargs, " -d", sizeof(diffargs)); 105219c50abSray dflags |= D_MINIMAL; 1066412cad2Ssthen break; 107be756b91Stobias case 'D': 108be756b91Stobias if (date1 == -1 && rev1 == NULL) { 109*092db204Sray if ((date1 = date_parse(optarg)) == -1) 110*092db204Sray fatal("invalid date: %s", optarg); 111909bf3e0Stobias dateflag1 = optarg; 112be756b91Stobias } else if (date2 == -1 && rev2 == NULL) { 113*092db204Sray if ((date2 = date_parse(optarg)) == -1) 114*092db204Sray fatal("invalid date: %s", optarg); 115909bf3e0Stobias dateflag2 = optarg; 116be756b91Stobias } else { 117be756b91Stobias fatal("no more than 2 revisions/dates can" 118be756b91Stobias " be specified"); 119be756b91Stobias } 120be756b91Stobias break; 121fd660bf2Stobias case 'f': 122fd660bf2Stobias force_head = 1; 123fd660bf2Stobias break; 124fd660bf2Stobias case 'i': 125fd660bf2Stobias strlcat(diffargs, " -i", sizeof(diffargs)); 126219c50abSray dflags |= D_IGNORECASE; 127fd660bf2Stobias break; 12837fdff3fStobias case 'k': 12937fdff3fStobias koptstr = optarg; 13037fdff3fStobias kflag = rcs_kflag_get(koptstr); 13137fdff3fStobias if (RCS_KWEXP_INVAL(kflag)) { 13237fdff3fStobias cvs_log(LP_ERR, 1330bc1d395Stobias "invalid RCS keyword expansion mode"); 134aeaaf4a6Stobias fatal("%s", cvs_cmdop == CVS_OP_DIFF ? 135aeaaf4a6Stobias cvs_cmd_diff.cmd_synopsis : 136aeaaf4a6Stobias cvs_cmd_rdiff.cmd_synopsis); 13737fdff3fStobias } 13837fdff3fStobias break; 1391890abdaSjoris case 'l': 1401890abdaSjoris flags &= ~CR_RECURSE_DIRS; 1411890abdaSjoris break; 142394180a4Sjfb case 'n': 143394180a4Sjfb strlcat(diffargs, " -n", sizeof(diffargs)); 144f9b67873Sniallo diff_format = D_RCSDIFF; 145394180a4Sjfb break; 14667caf486Sjoris case 'N': 147fd687d09Stobias strlcat(diffargs, " -N", sizeof(diffargs)); 1482d6f447eSjoris Nflag = 1; 14967caf486Sjoris break; 150261cb0daSjoris case 'p': 151261cb0daSjoris strlcat(diffargs, " -p", sizeof(diffargs)); 152219c50abSray dflags |= D_PROTOTYPE; 153261cb0daSjoris break; 154bcf22459Stobias case 'R': 155bcf22459Stobias flags |= CR_RECURSE_DIRS; 156bcf22459Stobias break; 15708f90673Sjfb case 'r': 158be756b91Stobias if (date1 == -1 && rev1 == NULL) { 1594230e8b3Sjoris rev1 = optarg; 160be756b91Stobias } else if (date2 == -1 && rev2 == NULL) { 1614230e8b3Sjoris rev2 = optarg; 16216cfc147Sjoris } else { 1633ad3fb45Sjoris fatal("no more than 2 revisions/dates can" 16408f90673Sjfb " be specified"); 16508f90673Sjfb } 16608f90673Sjfb break; 167219c50abSray case 't': 168219c50abSray strlcat(diffargs, " -t", sizeof(diffargs)); 169219c50abSray dflags |= D_EXPANDTABS; 170219c50abSray break; 17108f90673Sjfb case 'u': 172f5638424Sjfb strlcat(diffargs, " -u", sizeof(diffargs)); 173f9b67873Sniallo diff_format = D_UNIFIED; 17408f90673Sjfb break; 17572026f1aSnicm case 'U': 17672026f1aSnicm diff_context = strtonum(optarg, 0, INT_MAX, &errstr); 17772026f1aSnicm if (errstr != NULL) 17872026f1aSnicm fatal("context lines %s: %s", errstr, optarg); 17972026f1aSnicm strlcat(diffargs, " -U ", sizeof(diffargs)); 18072026f1aSnicm strlcat(diffargs, optarg, sizeof(diffargs)); 18172026f1aSnicm diff_format = D_UNIFIED; 18272026f1aSnicm break; 18337fdff3fStobias case 'V': 18437fdff3fStobias fatal("the -V option is obsolete " 18537fdff3fStobias "and should not be used"); 1866412cad2Ssthen case 'w': 1876412cad2Ssthen strlcat(diffargs, " -w", sizeof(diffargs)); 188219c50abSray dflags |= D_IGNOREBLANKS; 1896412cad2Ssthen break; 19008f90673Sjfb default: 191aeaaf4a6Stobias fatal("%s", cvs_cmdop == CVS_OP_DIFF ? 192aeaaf4a6Stobias cvs_cmd_diff.cmd_synopsis : 193aeaaf4a6Stobias cvs_cmd_rdiff.cmd_synopsis); 19408f90673Sjfb } 19508f90673Sjfb } 19608f90673Sjfb 1973ad3fb45Sjoris argc -= optind; 1983ad3fb45Sjoris argv += optind; 199dc6a6879Sjfb 2003ad3fb45Sjoris cr.enterdir = NULL; 2013ad3fb45Sjoris cr.leavedir = NULL; 2029fac60a5Sjoris 203fd660bf2Stobias if (cvs_cmdop == CVS_OP_RDIFF) { 204909bf3e0Stobias if (rev1 == NULL && rev2 == NULL && dateflag1 == NULL && 205909bf3e0Stobias dateflag2 == NULL) 206fd660bf2Stobias fatal("must specify at least one revision/date!"); 207fd660bf2Stobias 208fd687d09Stobias if (!argc) 209fd687d09Stobias fatal("%s", cvs_cmd_rdiff.cmd_synopsis); 210fd687d09Stobias 211fd660bf2Stobias if (!diff_format) { 212fd660bf2Stobias strlcat(diffargs, " -c", sizeof(diffargs)); 213fd660bf2Stobias diff_format = D_CONTEXT; 214fd660bf2Stobias } 215fd660bf2Stobias 216fd660bf2Stobias flags |= CR_REPO; 217fd660bf2Stobias } 218fd660bf2Stobias 2199fac60a5Sjoris if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 22080f6ca9bSjoris cvs_client_connect_to_server(); 2219fac60a5Sjoris cr.fileproc = cvs_client_sendfile; 2229fac60a5Sjoris 2239fac60a5Sjoris if (!(flags & CR_RECURSE_DIRS)) 2249fac60a5Sjoris cvs_client_send_request("Argument -l"); 2259fac60a5Sjoris 22637fdff3fStobias if (kflag) 22737fdff3fStobias cvs_client_send_request("Argument -k%s", koptstr); 22837fdff3fStobias 2299fac60a5Sjoris switch (diff_format) { 2309fac60a5Sjoris case D_CONTEXT: 23172026f1aSnicm if (cvs_cmdop == CVS_OP_RDIFF) 2329fac60a5Sjoris cvs_client_send_request("Argument -c"); 23372026f1aSnicm else { 23472026f1aSnicm cvs_client_send_request("Argument -C %d", 23572026f1aSnicm diff_context); 23672026f1aSnicm } 2379fac60a5Sjoris break; 2389fac60a5Sjoris case D_RCSDIFF: 2399fac60a5Sjoris cvs_client_send_request("Argument -n"); 2409fac60a5Sjoris break; 2419fac60a5Sjoris case D_UNIFIED: 24272026f1aSnicm if (cvs_cmdop == CVS_OP_RDIFF) 2439fac60a5Sjoris cvs_client_send_request("Argument -u"); 24472026f1aSnicm else { 24572026f1aSnicm cvs_client_send_request("Argument -U %d", 24672026f1aSnicm diff_context); 24772026f1aSnicm } 2489fac60a5Sjoris break; 2499fac60a5Sjoris default: 2509fac60a5Sjoris break; 2519fac60a5Sjoris } 2529fac60a5Sjoris 2539fac60a5Sjoris if (Nflag == 1) 2549fac60a5Sjoris cvs_client_send_request("Argument -N"); 2559fac60a5Sjoris 256219c50abSray if (dflags & D_PROTOTYPE) 2579fac60a5Sjoris cvs_client_send_request("Argument -p"); 2589fac60a5Sjoris 2599fac60a5Sjoris if (rev1 != NULL) 2609fac60a5Sjoris cvs_client_send_request("Argument -r%s", rev1); 2619fac60a5Sjoris if (rev2 != NULL) 2629fac60a5Sjoris cvs_client_send_request("Argument -r%s", rev2); 263909bf3e0Stobias 264909bf3e0Stobias if (dateflag1 != NULL) 265909bf3e0Stobias cvs_client_send_request("Argument -D%s", dateflag1); 266909bf3e0Stobias if (dateflag2 != NULL) 267909bf3e0Stobias cvs_client_send_request("Argument -D%s", dateflag2); 2689fac60a5Sjoris } else { 269fd660bf2Stobias if (cvs_cmdop == CVS_OP_RDIFF && 270fd660bf2Stobias chdir(current_cvsroot->cr_dir) == -1) 271fd660bf2Stobias fatal("cvs_diff: %s", strerror(errno)); 272fd660bf2Stobias 273bc5d89feSjoris cr.fileproc = cvs_diff_local; 2749fac60a5Sjoris } 2759fac60a5Sjoris 2761890abdaSjoris cr.flags = flags; 277dc6a6879Sjfb 2784230e8b3Sjoris diff_rev1 = diff_rev2 = NULL; 2794230e8b3Sjoris 280fd660bf2Stobias if (cvs_cmdop == CVS_OP_DIFF || 281fd660bf2Stobias current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 2823ad3fb45Sjoris if (argc > 0) 2833ad3fb45Sjoris cvs_file_run(argc, argv, &cr); 284d990145dSray else 2853ad3fb45Sjoris cvs_file_run(1, &arg, &cr); 286fd660bf2Stobias } 287e4276007Sjfb 2889fac60a5Sjoris if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) { 2899fac60a5Sjoris cvs_client_send_files(argv, argc); 2909fac60a5Sjoris cvs_client_senddir("."); 291fd660bf2Stobias 292fd660bf2Stobias cvs_client_send_request((cvs_cmdop == CVS_OP_RDIFF) ? 293fd660bf2Stobias "rdiff" : "diff"); 294fd660bf2Stobias 2959fac60a5Sjoris cvs_client_get_responses(); 2969fac60a5Sjoris } 2979fac60a5Sjoris 298e4276007Sjfb return (0); 299e4276007Sjfb } 300e4276007Sjfb 30101af718aSjoris void 3023ad3fb45Sjoris cvs_diff_local(struct cvs_file *cf) 303f9b67873Sniallo { 304741443cbSjoris BUF *b1; 3052e0d696aSjoris int fd1, fd2; 3063ad3fb45Sjoris struct stat st; 3073ad3fb45Sjoris struct timeval tv[2], tv2[2]; 3086534056aStobias struct tm datetm; 309be756b91Stobias char rbuf[CVS_REV_BUFSZ], tbuf[CVS_TIME_BUFSZ], *p1, *p2; 310d71a0c9bSxsa 311741443cbSjoris b1 = NULL; 312fd687d09Stobias fd1 = fd2 = -1; 313fd687d09Stobias p1 = p2 = NULL; 314f9b67873Sniallo 3153ad3fb45Sjoris cvs_log(LP_TRACE, "cvs_diff_local(%s)", cf->file_path); 3163ad3fb45Sjoris 3173ad3fb45Sjoris if (cf->file_type == CVS_DIR) { 3183ad3fb45Sjoris if (verbosity > 1) 3191489d205Sjoris cvs_log(LP_ERR, "Diffing inside %s", cf->file_path); 3203ad3fb45Sjoris return; 3213ad3fb45Sjoris } 3223ad3fb45Sjoris 32351ef6581Sjoris cvs_file_classify(cf, cvs_directory_tag); 3243ad3fb45Sjoris 325fd687d09Stobias if (cvs_cmdop == CVS_OP_DIFF) { 326fd687d09Stobias if (cf->file_ent == NULL) { 327fd687d09Stobias cvs_log(LP_ERR, "I know nothing about %s", 32867caf486Sjoris cf->file_path); 3293ad3fb45Sjoris return; 33067caf486Sjoris } 3313ad3fb45Sjoris 332fd687d09Stobias switch (cf->file_ent->ce_status) { 333fd687d09Stobias case CVS_ENT_ADDED: 334fd687d09Stobias if (Nflag == 0) { 335fd687d09Stobias cvs_log(LP_ERR, "%s is a new entry, no " 336fd687d09Stobias "comparison available", cf->file_path); 337fd687d09Stobias return; 338fd687d09Stobias } 3395cf15c45Sjoris if (!(cf->file_flags & FILE_ON_DISK)) { 340fd687d09Stobias cvs_log(LP_ERR, "cannot find %s", 341fd687d09Stobias cf->file_path); 342fd687d09Stobias return; 343fd687d09Stobias } 344fd687d09Stobias break; 345fd687d09Stobias case CVS_ENT_REMOVED: 346fd687d09Stobias if (Nflag == 0) { 347fd687d09Stobias cvs_log(LP_ERR, "%s was removed, no " 348fd687d09Stobias "comparison available", cf->file_path); 349fd687d09Stobias return; 350fd687d09Stobias } 351fd687d09Stobias if (cf->file_rcs == NULL) { 352fd687d09Stobias cvs_log(LP_ERR, "cannot find RCS file for %s", 353fd687d09Stobias cf->file_path); 354fd687d09Stobias return; 355fd687d09Stobias } 356fd687d09Stobias break; 357fd687d09Stobias default: 3585cf15c45Sjoris if (!(cf->file_flags & FILE_ON_DISK)) { 3591489d205Sjoris cvs_printf("? %s\n", cf->file_path); 360fd687d09Stobias return; 361fd687d09Stobias } 362c638a04bSjoris 363fd687d09Stobias if (cf->file_rcs == NULL) { 364fd687d09Stobias cvs_log(LP_ERR, "cannot find RCS file for %s", 365fd687d09Stobias cf->file_path); 366fd687d09Stobias return; 367fd687d09Stobias } 368fd687d09Stobias break; 369fd687d09Stobias } 370fd687d09Stobias } 371fd687d09Stobias 372be756b91Stobias if (cf->file_status == FILE_UPTODATE && rev1 == NULL && rev2 == NULL && 373be756b91Stobias date1 == -1 && date2 == -1) 374fd687d09Stobias return; 375fd687d09Stobias 376ebd92267Sjoris if (cf->file_rcs != NULL && cf->file_rcs->rf_head == NULL) { 377e28eda4eStobias cvs_log(LP_ERR, "no head revision in RCS file for %s\n", 378e28eda4eStobias cf->file_path); 379e28eda4eStobias return; 380e28eda4eStobias } 381e28eda4eStobias 382fd687d09Stobias if (kflag && cf->file_rcs != NULL) 38337fdff3fStobias rcs_kwexp_set(cf->file_rcs, kflag); 38437fdff3fStobias 385fd687d09Stobias if (cf->file_rcs == NULL) 386fd687d09Stobias diff_rev1 = NULL; 387be756b91Stobias else if (rev1 != NULL || date1 != -1) { 388be756b91Stobias cvs_specified_date = date1; 389fd687d09Stobias diff_rev1 = rcs_translate_tag(rev1, cf->file_rcs); 390fd687d09Stobias if (diff_rev1 == NULL && cvs_cmdop == CVS_OP_DIFF) { 391223285fdStobias if (rev1 != NULL) { 392fd687d09Stobias cvs_log(LP_ERR, "tag %s not in file %s", rev1, 393fd687d09Stobias cf->file_path); 394223285fdStobias goto cleanup; 395223285fdStobias } else if (Nflag) { 396223285fdStobias diff_rev1 = NULL; 397223285fdStobias } else { 3986534056aStobias gmtime_r(&cvs_specified_date, &datetm); 399be756b91Stobias strftime(tbuf, sizeof(tbuf), 4006534056aStobias "%Y.%m.%d.%H.%M.%S", &datetm); 401be756b91Stobias cvs_log(LP_ERR, "no revision for date %s in " 402be756b91Stobias "file %s", tbuf, cf->file_path); 403fd687d09Stobias goto cleanup; 404223285fdStobias } 405fd687d09Stobias } else if (diff_rev1 == NULL && cvs_cmdop == CVS_OP_RDIFF && 406fd687d09Stobias force_head) { 407fd660bf2Stobias /* -f is not allowed for unknown symbols */ 408fd687d09Stobias if ((diff_rev1 = rcsnum_parse(rev1)) == NULL) 409fd660bf2Stobias fatal("no such tag %s", rev1); 410fd660bf2Stobias rcsnum_free(diff_rev1); 411fd660bf2Stobias 412fd687d09Stobias diff_rev1 = cf->file_rcs->rf_head; 413fd660bf2Stobias } 414be756b91Stobias cvs_specified_date = -1; 415fd687d09Stobias } else if (cvs_cmdop == CVS_OP_DIFF) { 416fd687d09Stobias if (cf->file_ent->ce_status == CVS_ENT_ADDED) 417fd687d09Stobias diff_rev1 = NULL; 418fd687d09Stobias else 419fd687d09Stobias diff_rev1 = cf->file_ent->ce_rev; 420fd660bf2Stobias } 42151ef6581Sjoris 422fd687d09Stobias if (cf->file_rcs == NULL) 423fd687d09Stobias diff_rev2 = NULL; 424be756b91Stobias else if (rev2 != NULL || date2 != -1) { 425be756b91Stobias cvs_specified_date = date2; 426fd687d09Stobias diff_rev2 = rcs_translate_tag(rev2, cf->file_rcs); 427fd687d09Stobias if (diff_rev2 == NULL && cvs_cmdop == CVS_OP_DIFF) { 428be756b91Stobias if (rev2 != NULL) { 429fd687d09Stobias cvs_log(LP_ERR, "tag %s not in file %s", rev2, 430fd687d09Stobias cf->file_path); 431223285fdStobias goto cleanup; 432223285fdStobias } else if (Nflag) { 433223285fdStobias diff_rev2 = NULL; 434be756b91Stobias } else { 4356534056aStobias gmtime_r(&cvs_specified_date, &datetm); 436be756b91Stobias strftime(tbuf, sizeof(tbuf), 4376534056aStobias "%Y.%m.%d.%H.%M.%S", &datetm); 438be756b91Stobias cvs_log(LP_ERR, "no revision for date %s in " 439be756b91Stobias "file %s", tbuf, cf->file_path); 440fd687d09Stobias goto cleanup; 441223285fdStobias } 442fd687d09Stobias } else if (diff_rev2 == NULL && cvs_cmdop == CVS_OP_RDIFF && 443fd687d09Stobias force_head) { 444fd660bf2Stobias /* -f is not allowed for unknown symbols */ 445fd687d09Stobias if ((diff_rev2 = rcsnum_parse(rev2)) == NULL) 446fd660bf2Stobias fatal("no such tag %s", rev2); 447fd660bf2Stobias rcsnum_free(diff_rev2); 448fd660bf2Stobias 449fd687d09Stobias diff_rev2 = cf->file_rcs->rf_head; 450fd660bf2Stobias } 451be756b91Stobias cvs_specified_date = -1; 452fd687d09Stobias } else if (cvs_cmdop == CVS_OP_RDIFF) 453fd687d09Stobias diff_rev2 = cf->file_rcs->rf_head; 454fd687d09Stobias else if (cf->file_ent->ce_status == CVS_ENT_REMOVED) 455fd687d09Stobias diff_rev2 = NULL; 456fd687d09Stobias 457fd687d09Stobias if (diff_rev1 != NULL && diff_rev2 != NULL && 458fd687d09Stobias rcsnum_cmp(diff_rev1, diff_rev2, 0) == 0) 459fd687d09Stobias goto cleanup; 460fd687d09Stobias 461fd687d09Stobias switch (cvs_cmdop) { 462fd687d09Stobias case CVS_OP_DIFF: 46352a66329Sjoris if (cf->file_status == FILE_UPTODATE) { 46452a66329Sjoris if (diff_rev2 == NULL && 46552a66329Sjoris !rcsnum_cmp(diff_rev1, cf->file_rcsrev, 0)) 46652a66329Sjoris goto cleanup; 46752a66329Sjoris } 468fd687d09Stobias break; 469fd687d09Stobias case CVS_OP_RDIFF: 470fd687d09Stobias if (diff_rev1 == NULL && diff_rev2 == NULL) 471fd687d09Stobias goto cleanup; 472fd687d09Stobias break; 473fd660bf2Stobias } 474fd660bf2Stobias 475fd660bf2Stobias cvs_printf("Index: %s\n", cf->file_path); 476fd660bf2Stobias if (cvs_cmdop == CVS_OP_DIFF) 477fd687d09Stobias cvs_printf("%s\nRCS file: %s\n", RCS_DIFF_DIV, 478fd687d09Stobias cf->file_rcs != NULL ? cf->file_rpath : cf->file_path); 479fd660bf2Stobias 480fd660bf2Stobias if (diff_rev1 != NULL) { 481fd687d09Stobias if (cvs_cmdop == CVS_OP_DIFF && diff_rev1 != NULL) { 482fd687d09Stobias (void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf)); 483fd687d09Stobias cvs_printf("retrieving revision %s\n", rbuf); 484fd687d09Stobias } 4853ad3fb45Sjoris 486fd687d09Stobias tv[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev1); 4873ad3fb45Sjoris tv[0].tv_usec = 0; 4883ad3fb45Sjoris tv[1] = tv[0]; 489741443cbSjoris 490fd687d09Stobias (void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); 491fd687d09Stobias fd1 = rcs_rev_write_stmp(cf->file_rcs, diff_rev1, p1, 0); 4922e0d696aSjoris if (futimes(fd1, tv) == -1) 493fd660bf2Stobias fatal("cvs_diff_local: utimes failed"); 494fd660bf2Stobias } 4953ad3fb45Sjoris 496fd687d09Stobias if (diff_rev2 != NULL) { 497fd687d09Stobias if (cvs_cmdop == CVS_OP_DIFF && rev2 != NULL) { 498fd660bf2Stobias (void)rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf)); 499fd687d09Stobias cvs_printf("retrieving revision %s\n", rbuf); 500fd687d09Stobias } 5013ad3fb45Sjoris 5023ad3fb45Sjoris tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev2); 5033ad3fb45Sjoris tv2[0].tv_usec = 0; 5043ad3fb45Sjoris tv2[1] = tv2[0]; 505741443cbSjoris 506fd687d09Stobias (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 5072e0d696aSjoris fd2 = rcs_rev_write_stmp(cf->file_rcs, diff_rev2, p2, 0); 5082e0d696aSjoris if (futimes(fd2, tv2) == -1) 509fd660bf2Stobias fatal("cvs_diff_local: utimes failed"); 5105cf15c45Sjoris } else if (cvs_cmdop == CVS_OP_DIFF && 5115cf15c45Sjoris (cf->file_flags & FILE_ON_DISK) && 512fd687d09Stobias cf->file_ent->ce_status != CVS_ENT_REMOVED) { 513e91b8a58Sjoris (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 514e91b8a58Sjoris if (cvs_server_active == 1 && cf->fd == -1) { 515e91b8a58Sjoris tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, 516e91b8a58Sjoris cf->file_ent->ce_rev); 517e91b8a58Sjoris tv2[0].tv_usec = 0; 518e91b8a58Sjoris tv2[1] = tv2[0]; 519e91b8a58Sjoris 520e91b8a58Sjoris fd2 = rcs_rev_write_stmp(cf->file_rcs, 521e91b8a58Sjoris cf->file_ent->ce_rev, p2, 0); 522e91b8a58Sjoris if (futimes(fd2, tv2) == -1) 523e91b8a58Sjoris fatal("cvs_diff_local: futimes failed"); 524e91b8a58Sjoris } else { 5253ad3fb45Sjoris if (fstat(cf->fd, &st) == -1) 5263ad3fb45Sjoris fatal("fstat failed %s", strerror(errno)); 5277bb3ddb0Sray b1 = buf_load_fd(cf->fd); 5283ad3fb45Sjoris 5293ad3fb45Sjoris tv2[0].tv_sec = st.st_mtime; 5303ad3fb45Sjoris tv2[0].tv_usec = 0; 5313ad3fb45Sjoris tv2[1] = tv2[0]; 532741443cbSjoris 5337bb3ddb0Sray fd2 = buf_write_stmp(b1, p2, tv2); 5347bb3ddb0Sray buf_free(b1); 5353ad3fb45Sjoris } 536e91b8a58Sjoris } 5373ad3fb45Sjoris 538fd687d09Stobias switch (cvs_cmdop) { 539fd687d09Stobias case CVS_OP_DIFF: 5403ad3fb45Sjoris cvs_printf("%s", diffargs); 5413ad3fb45Sjoris 542fd687d09Stobias if (rev1 != NULL && diff_rev1 != NULL) { 543fd687d09Stobias (void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf)); 5443ad3fb45Sjoris cvs_printf(" -r%s", rbuf); 5453ad3fb45Sjoris 546fd687d09Stobias if (rev2 != NULL && diff_rev2 != NULL) { 547fd660bf2Stobias (void)rcsnum_tostr(diff_rev2, rbuf, 548fd660bf2Stobias sizeof(rbuf)); 5493ad3fb45Sjoris cvs_printf(" -r%s", rbuf); 5503ad3fb45Sjoris } 55167caf486Sjoris } 5523ad3fb45Sjoris 553fd660bf2Stobias if (diff_rev2 == NULL) 554fd687d09Stobias cvs_printf(" %s", cf->file_path); 55509d59ca3Stobias cvs_printf("\n"); 556fd687d09Stobias break; 557fd687d09Stobias case CVS_OP_RDIFF: 558fd660bf2Stobias cvs_printf("diff "); 559fd660bf2Stobias switch (diff_format) { 560fd660bf2Stobias case D_CONTEXT: 561fd660bf2Stobias cvs_printf("-c "); 562fd660bf2Stobias break; 563fd660bf2Stobias case D_RCSDIFF: 564fd660bf2Stobias cvs_printf("-n "); 565fd660bf2Stobias break; 566fd660bf2Stobias case D_UNIFIED: 567fd660bf2Stobias cvs_printf("-u "); 568fd660bf2Stobias break; 569fd660bf2Stobias default: 570fd660bf2Stobias break; 571fd660bf2Stobias } 572fd660bf2Stobias if (diff_rev1 == NULL) { 573fd660bf2Stobias cvs_printf("%s ", CVS_PATH_DEVNULL); 574fd660bf2Stobias } else { 575fd660bf2Stobias (void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf)); 576fd660bf2Stobias cvs_printf("%s:%s ", cf->file_path, rbuf); 577fd660bf2Stobias } 5783ad3fb45Sjoris 579fd660bf2Stobias if (diff_rev2 == NULL) { 580fd660bf2Stobias cvs_printf("%s:removed\n", cf->file_path); 581fd660bf2Stobias } else { 582fd660bf2Stobias (void)rcsnum_tostr(diff_rev2 != NULL ? diff_rev2 : 583fd660bf2Stobias cf->file_rcs->rf_head, rbuf, sizeof(rbuf)); 584fd660bf2Stobias cvs_printf("%s:%s\n", cf->file_path, rbuf); 585fd660bf2Stobias } 586fd687d09Stobias break; 587fd660bf2Stobias } 588fd660bf2Stobias 589fd687d09Stobias if (fd1 == -1) { 590fd687d09Stobias if ((fd1 = open(CVS_PATH_DEVNULL, O_RDONLY, 0)) == -1) 591fd687d09Stobias fatal("cannot open %s", CVS_PATH_DEVNULL); 592fd687d09Stobias } 593fd687d09Stobias if (fd2 == -1) { 594fd687d09Stobias if ((fd2 = open(CVS_PATH_DEVNULL, O_RDONLY, 0)) == -1) 595fd687d09Stobias fatal("cannot open %s", CVS_PATH_DEVNULL); 596741443cbSjoris } 5973ad3fb45Sjoris 59857003866Sray if (diffreg(p1 != NULL ? cf->file_path : CVS_PATH_DEVNULL, 599219c50abSray p2 != NULL ? cf->file_path : CVS_PATH_DEVNULL, fd1, fd2, NULL, 600219c50abSray dflags) == D_ERROR) 601d71a0c9bSxsa fatal("cvs_diff_local: failed to get RCS patch"); 602d71a0c9bSxsa 6032e0d696aSjoris close(fd1); 6042e0d696aSjoris close(fd2); 6052e0d696aSjoris 6067a9e6d11Sray worklist_run(&temp_files, worklist_unlink); 60767caf486Sjoris 608d71a0c9bSxsa if (p1 != NULL) 609d71a0c9bSxsa xfree(p1); 610d71a0c9bSxsa if (p2 != NULL) 611d71a0c9bSxsa xfree(p2); 612d71a0c9bSxsa 613fd687d09Stobias cleanup: 614fd687d09Stobias if (diff_rev1 != NULL && 615fd687d09Stobias (cf->file_rcs == NULL || diff_rev1 != cf->file_rcs->rf_head) && 616fd687d09Stobias (cf->file_ent == NULL || diff_rev1 != cf->file_ent->ce_rev)) 617fd687d09Stobias xfree(diff_rev1); 618fd687d09Stobias diff_rev1 = NULL; 619261cb0daSjoris 620fd687d09Stobias if (diff_rev2 != NULL && 621fd687d09Stobias (cf->file_rcs == NULL || diff_rev2 != cf->file_rcs->rf_head)) 622fd687d09Stobias xfree(diff_rev2); 623fd687d09Stobias diff_rev2 = NULL; 624f9b67873Sniallo } 625