1*b7041c07Sderaadt /* $OpenBSD: diff.c,v 1.164 2021/10/24 21:24:16 deraadt 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) { 109092db204Sray if ((date1 = date_parse(optarg)) == -1) 110092db204Sray fatal("invalid date: %s", optarg); 111909bf3e0Stobias dateflag1 = optarg; 112be756b91Stobias } else if (date2 == -1 && rev2 == NULL) { 113092db204Sray if ((date2 = date_parse(optarg)) == -1) 114092db204Sray 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 2194dcde513Sjoris if (cvsroot_is_remote()) { 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: 24272d09e4aSnicm if (cvs_cmdop == CVS_OP_RDIFF || diff_context == 3) 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 2804dcde513Sjoris if (cvs_cmdop == CVS_OP_DIFF || cvsroot_is_local()) { 2813ad3fb45Sjoris if (argc > 0) 2823ad3fb45Sjoris cvs_file_run(argc, argv, &cr); 283d990145dSray else 2843ad3fb45Sjoris cvs_file_run(1, &arg, &cr); 285fd660bf2Stobias } 286e4276007Sjfb 2874dcde513Sjoris if (cvsroot_is_remote()) { 2889fac60a5Sjoris cvs_client_send_files(argv, argc); 2899fac60a5Sjoris cvs_client_senddir("."); 290fd660bf2Stobias 291fd660bf2Stobias cvs_client_send_request((cvs_cmdop == CVS_OP_RDIFF) ? 292fd660bf2Stobias "rdiff" : "diff"); 293fd660bf2Stobias 2949fac60a5Sjoris cvs_client_get_responses(); 2959fac60a5Sjoris } 2969fac60a5Sjoris 297e4276007Sjfb return (0); 298e4276007Sjfb } 299e4276007Sjfb 30001af718aSjoris void 3013ad3fb45Sjoris cvs_diff_local(struct cvs_file *cf) 302f9b67873Sniallo { 303741443cbSjoris BUF *b1; 3042e0d696aSjoris int fd1, fd2; 3053ad3fb45Sjoris struct stat st; 3063ad3fb45Sjoris struct timeval tv[2], tv2[2]; 3076534056aStobias struct tm datetm; 308be756b91Stobias char rbuf[CVS_REV_BUFSZ], tbuf[CVS_TIME_BUFSZ], *p1, *p2; 309d71a0c9bSxsa 310741443cbSjoris b1 = NULL; 311fd687d09Stobias fd1 = fd2 = -1; 312fd687d09Stobias p1 = p2 = NULL; 313f9b67873Sniallo 3143ad3fb45Sjoris cvs_log(LP_TRACE, "cvs_diff_local(%s)", cf->file_path); 3153ad3fb45Sjoris 3163ad3fb45Sjoris if (cf->file_type == CVS_DIR) { 3173ad3fb45Sjoris if (verbosity > 1) 3181489d205Sjoris cvs_log(LP_ERR, "Diffing inside %s", cf->file_path); 3193ad3fb45Sjoris return; 3203ad3fb45Sjoris } 3213ad3fb45Sjoris 32251ef6581Sjoris cvs_file_classify(cf, cvs_directory_tag); 3233ad3fb45Sjoris 324fd687d09Stobias if (cvs_cmdop == CVS_OP_DIFF) { 325fd687d09Stobias if (cf->file_ent == NULL) { 326fd687d09Stobias cvs_log(LP_ERR, "I know nothing about %s", 32767caf486Sjoris cf->file_path); 3283ad3fb45Sjoris return; 32967caf486Sjoris } 3303ad3fb45Sjoris 331fd687d09Stobias switch (cf->file_ent->ce_status) { 332fd687d09Stobias case CVS_ENT_ADDED: 333fd687d09Stobias if (Nflag == 0) { 334fd687d09Stobias cvs_log(LP_ERR, "%s is a new entry, no " 335fd687d09Stobias "comparison available", cf->file_path); 336fd687d09Stobias return; 337fd687d09Stobias } 3385cf15c45Sjoris if (!(cf->file_flags & FILE_ON_DISK)) { 339fd687d09Stobias cvs_log(LP_ERR, "cannot find %s", 340fd687d09Stobias cf->file_path); 341fd687d09Stobias return; 342fd687d09Stobias } 343fd687d09Stobias break; 344fd687d09Stobias case CVS_ENT_REMOVED: 345fd687d09Stobias if (Nflag == 0) { 346fd687d09Stobias cvs_log(LP_ERR, "%s was removed, no " 347fd687d09Stobias "comparison available", cf->file_path); 348fd687d09Stobias return; 349fd687d09Stobias } 350fd687d09Stobias if (cf->file_rcs == NULL) { 351fd687d09Stobias cvs_log(LP_ERR, "cannot find RCS file for %s", 352fd687d09Stobias cf->file_path); 353fd687d09Stobias return; 354fd687d09Stobias } 355fd687d09Stobias break; 356fd687d09Stobias default: 3575cf15c45Sjoris if (!(cf->file_flags & FILE_ON_DISK)) { 3581489d205Sjoris cvs_printf("? %s\n", cf->file_path); 359fd687d09Stobias return; 360fd687d09Stobias } 361c638a04bSjoris 362fd687d09Stobias if (cf->file_rcs == NULL) { 363fd687d09Stobias cvs_log(LP_ERR, "cannot find RCS file for %s", 364fd687d09Stobias cf->file_path); 365fd687d09Stobias return; 366fd687d09Stobias } 367fd687d09Stobias break; 368fd687d09Stobias } 369fd687d09Stobias } 370fd687d09Stobias 371be756b91Stobias if (cf->file_status == FILE_UPTODATE && rev1 == NULL && rev2 == NULL && 372be756b91Stobias date1 == -1 && date2 == -1) 373fd687d09Stobias return; 374fd687d09Stobias 375ebd92267Sjoris if (cf->file_rcs != NULL && cf->file_rcs->rf_head == NULL) { 376e28eda4eStobias cvs_log(LP_ERR, "no head revision in RCS file for %s\n", 377e28eda4eStobias cf->file_path); 378e28eda4eStobias return; 379e28eda4eStobias } 380e28eda4eStobias 381fd687d09Stobias if (kflag && cf->file_rcs != NULL) 38237fdff3fStobias rcs_kwexp_set(cf->file_rcs, kflag); 38337fdff3fStobias 384fd687d09Stobias if (cf->file_rcs == NULL) 385fd687d09Stobias diff_rev1 = NULL; 386be756b91Stobias else if (rev1 != NULL || date1 != -1) { 387be756b91Stobias cvs_specified_date = date1; 388fd687d09Stobias diff_rev1 = rcs_translate_tag(rev1, cf->file_rcs); 389fd687d09Stobias if (diff_rev1 == NULL && cvs_cmdop == CVS_OP_DIFF) { 390223285fdStobias if (rev1 != NULL) { 391fd687d09Stobias cvs_log(LP_ERR, "tag %s not in file %s", rev1, 392fd687d09Stobias cf->file_path); 393223285fdStobias goto cleanup; 394223285fdStobias } else if (Nflag) { 395223285fdStobias diff_rev1 = NULL; 396223285fdStobias } else { 3976534056aStobias gmtime_r(&cvs_specified_date, &datetm); 398be756b91Stobias strftime(tbuf, sizeof(tbuf), 3996534056aStobias "%Y.%m.%d.%H.%M.%S", &datetm); 400be756b91Stobias cvs_log(LP_ERR, "no revision for date %s in " 401be756b91Stobias "file %s", tbuf, cf->file_path); 402fd687d09Stobias goto cleanup; 403223285fdStobias } 404fd687d09Stobias } else if (diff_rev1 == NULL && cvs_cmdop == CVS_OP_RDIFF && 405fd687d09Stobias force_head) { 406fd660bf2Stobias /* -f is not allowed for unknown symbols */ 407fd687d09Stobias if ((diff_rev1 = rcsnum_parse(rev1)) == NULL) 408fd660bf2Stobias fatal("no such tag %s", rev1); 40953ce2177Sfcambus free(diff_rev1); 410fd660bf2Stobias 411fd687d09Stobias diff_rev1 = cf->file_rcs->rf_head; 412fd660bf2Stobias } 413be756b91Stobias cvs_specified_date = -1; 414fd687d09Stobias } else if (cvs_cmdop == CVS_OP_DIFF) { 415fd687d09Stobias if (cf->file_ent->ce_status == CVS_ENT_ADDED) 416fd687d09Stobias diff_rev1 = NULL; 417fd687d09Stobias else 418fd687d09Stobias diff_rev1 = cf->file_ent->ce_rev; 419fd660bf2Stobias } 42051ef6581Sjoris 421fd687d09Stobias if (cf->file_rcs == NULL) 422fd687d09Stobias diff_rev2 = NULL; 423be756b91Stobias else if (rev2 != NULL || date2 != -1) { 424be756b91Stobias cvs_specified_date = date2; 425fd687d09Stobias diff_rev2 = rcs_translate_tag(rev2, cf->file_rcs); 426fd687d09Stobias if (diff_rev2 == NULL && cvs_cmdop == CVS_OP_DIFF) { 427be756b91Stobias if (rev2 != NULL) { 428fd687d09Stobias cvs_log(LP_ERR, "tag %s not in file %s", rev2, 429fd687d09Stobias cf->file_path); 430223285fdStobias goto cleanup; 431223285fdStobias } else if (Nflag) { 432223285fdStobias diff_rev2 = NULL; 433be756b91Stobias } else { 4346534056aStobias gmtime_r(&cvs_specified_date, &datetm); 435be756b91Stobias strftime(tbuf, sizeof(tbuf), 4366534056aStobias "%Y.%m.%d.%H.%M.%S", &datetm); 437be756b91Stobias cvs_log(LP_ERR, "no revision for date %s in " 438be756b91Stobias "file %s", tbuf, cf->file_path); 439fd687d09Stobias goto cleanup; 440223285fdStobias } 441fd687d09Stobias } else if (diff_rev2 == NULL && cvs_cmdop == CVS_OP_RDIFF && 442fd687d09Stobias force_head) { 443fd660bf2Stobias /* -f is not allowed for unknown symbols */ 444fd687d09Stobias if ((diff_rev2 = rcsnum_parse(rev2)) == NULL) 445fd660bf2Stobias fatal("no such tag %s", rev2); 44653ce2177Sfcambus free(diff_rev2); 447fd660bf2Stobias 448fd687d09Stobias diff_rev2 = cf->file_rcs->rf_head; 449fd660bf2Stobias } 450be756b91Stobias cvs_specified_date = -1; 451fd687d09Stobias } else if (cvs_cmdop == CVS_OP_RDIFF) 452fd687d09Stobias diff_rev2 = cf->file_rcs->rf_head; 453fd687d09Stobias else if (cf->file_ent->ce_status == CVS_ENT_REMOVED) 454fd687d09Stobias diff_rev2 = NULL; 455fd687d09Stobias 456fd687d09Stobias if (diff_rev1 != NULL && diff_rev2 != NULL && 457fd687d09Stobias rcsnum_cmp(diff_rev1, diff_rev2, 0) == 0) 458fd687d09Stobias goto cleanup; 459fd687d09Stobias 460fd687d09Stobias switch (cvs_cmdop) { 461fd687d09Stobias case CVS_OP_DIFF: 46252a66329Sjoris if (cf->file_status == FILE_UPTODATE) { 46352a66329Sjoris if (diff_rev2 == NULL && 46452a66329Sjoris !rcsnum_cmp(diff_rev1, cf->file_rcsrev, 0)) 46552a66329Sjoris goto cleanup; 46652a66329Sjoris } 467fd687d09Stobias break; 468fd687d09Stobias case CVS_OP_RDIFF: 469fd687d09Stobias if (diff_rev1 == NULL && diff_rev2 == NULL) 470fd687d09Stobias goto cleanup; 471fd687d09Stobias break; 472fd660bf2Stobias } 473fd660bf2Stobias 474fd660bf2Stobias cvs_printf("Index: %s\n", cf->file_path); 475fd660bf2Stobias if (cvs_cmdop == CVS_OP_DIFF) 476fd687d09Stobias cvs_printf("%s\nRCS file: %s\n", RCS_DIFF_DIV, 477fd687d09Stobias cf->file_rcs != NULL ? cf->file_rpath : cf->file_path); 478fd660bf2Stobias 479fd660bf2Stobias if (diff_rev1 != NULL) { 480fd687d09Stobias if (cvs_cmdop == CVS_OP_DIFF && diff_rev1 != NULL) { 481fd687d09Stobias (void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf)); 482fd687d09Stobias cvs_printf("retrieving revision %s\n", rbuf); 483fd687d09Stobias } 4843ad3fb45Sjoris 485fd687d09Stobias tv[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev1); 4863ad3fb45Sjoris tv[0].tv_usec = 0; 4873ad3fb45Sjoris tv[1] = tv[0]; 488741443cbSjoris 489fd687d09Stobias (void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); 490fd687d09Stobias fd1 = rcs_rev_write_stmp(cf->file_rcs, diff_rev1, p1, 0); 4912e0d696aSjoris if (futimes(fd1, tv) == -1) 492fd660bf2Stobias fatal("cvs_diff_local: utimes failed"); 493fd660bf2Stobias } 4943ad3fb45Sjoris 495fd687d09Stobias if (diff_rev2 != NULL) { 496fd687d09Stobias if (cvs_cmdop == CVS_OP_DIFF && rev2 != NULL) { 497fd660bf2Stobias (void)rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf)); 498fd687d09Stobias cvs_printf("retrieving revision %s\n", rbuf); 499fd687d09Stobias } 5003ad3fb45Sjoris 5013ad3fb45Sjoris tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev2); 5023ad3fb45Sjoris tv2[0].tv_usec = 0; 5033ad3fb45Sjoris tv2[1] = tv2[0]; 504741443cbSjoris 505fd687d09Stobias (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 5062e0d696aSjoris fd2 = rcs_rev_write_stmp(cf->file_rcs, diff_rev2, p2, 0); 5072e0d696aSjoris if (futimes(fd2, tv2) == -1) 508fd660bf2Stobias fatal("cvs_diff_local: utimes failed"); 5095cf15c45Sjoris } else if (cvs_cmdop == CVS_OP_DIFF && 5105cf15c45Sjoris (cf->file_flags & FILE_ON_DISK) && 511fd687d09Stobias cf->file_ent->ce_status != CVS_ENT_REMOVED) { 512e91b8a58Sjoris (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); 513e91b8a58Sjoris if (cvs_server_active == 1 && cf->fd == -1) { 514e91b8a58Sjoris tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, 515e91b8a58Sjoris cf->file_ent->ce_rev); 516e91b8a58Sjoris tv2[0].tv_usec = 0; 517e91b8a58Sjoris tv2[1] = tv2[0]; 518e91b8a58Sjoris 519e91b8a58Sjoris fd2 = rcs_rev_write_stmp(cf->file_rcs, 520e91b8a58Sjoris cf->file_ent->ce_rev, p2, 0); 521e91b8a58Sjoris if (futimes(fd2, tv2) == -1) 522e91b8a58Sjoris fatal("cvs_diff_local: futimes failed"); 523e91b8a58Sjoris } else { 5243ad3fb45Sjoris if (fstat(cf->fd, &st) == -1) 5253ad3fb45Sjoris fatal("fstat failed %s", strerror(errno)); 5267bb3ddb0Sray b1 = buf_load_fd(cf->fd); 5273ad3fb45Sjoris 5283ad3fb45Sjoris tv2[0].tv_sec = st.st_mtime; 5293ad3fb45Sjoris tv2[0].tv_usec = 0; 5303ad3fb45Sjoris tv2[1] = tv2[0]; 531741443cbSjoris 5327bb3ddb0Sray fd2 = buf_write_stmp(b1, p2, tv2); 5337bb3ddb0Sray buf_free(b1); 5343ad3fb45Sjoris } 535e91b8a58Sjoris } 5363ad3fb45Sjoris 537fd687d09Stobias switch (cvs_cmdop) { 538fd687d09Stobias case CVS_OP_DIFF: 5393ad3fb45Sjoris cvs_printf("%s", diffargs); 5403ad3fb45Sjoris 541fd687d09Stobias if (rev1 != NULL && diff_rev1 != NULL) { 542fd687d09Stobias (void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf)); 5433ad3fb45Sjoris cvs_printf(" -r%s", rbuf); 5443ad3fb45Sjoris 545fd687d09Stobias if (rev2 != NULL && diff_rev2 != NULL) { 546fd660bf2Stobias (void)rcsnum_tostr(diff_rev2, rbuf, 547fd660bf2Stobias sizeof(rbuf)); 5483ad3fb45Sjoris cvs_printf(" -r%s", rbuf); 5493ad3fb45Sjoris } 55067caf486Sjoris } 5513ad3fb45Sjoris 552fd660bf2Stobias if (diff_rev2 == NULL) 553fd687d09Stobias cvs_printf(" %s", cf->file_path); 55409d59ca3Stobias cvs_printf("\n"); 555fd687d09Stobias break; 556fd687d09Stobias case CVS_OP_RDIFF: 557fd660bf2Stobias cvs_printf("diff "); 558fd660bf2Stobias switch (diff_format) { 559fd660bf2Stobias case D_CONTEXT: 560fd660bf2Stobias cvs_printf("-c "); 561fd660bf2Stobias break; 562fd660bf2Stobias case D_RCSDIFF: 563fd660bf2Stobias cvs_printf("-n "); 564fd660bf2Stobias break; 565fd660bf2Stobias case D_UNIFIED: 566fd660bf2Stobias cvs_printf("-u "); 567fd660bf2Stobias break; 568fd660bf2Stobias default: 569fd660bf2Stobias break; 570fd660bf2Stobias } 571fd660bf2Stobias if (diff_rev1 == NULL) { 572fd660bf2Stobias cvs_printf("%s ", CVS_PATH_DEVNULL); 573fd660bf2Stobias } else { 574fd660bf2Stobias (void)rcsnum_tostr(diff_rev1, rbuf, sizeof(rbuf)); 575fd660bf2Stobias cvs_printf("%s:%s ", cf->file_path, rbuf); 576fd660bf2Stobias } 5773ad3fb45Sjoris 578fd660bf2Stobias if (diff_rev2 == NULL) { 579fd660bf2Stobias cvs_printf("%s:removed\n", cf->file_path); 580fd660bf2Stobias } else { 581fd660bf2Stobias (void)rcsnum_tostr(diff_rev2 != NULL ? diff_rev2 : 582fd660bf2Stobias cf->file_rcs->rf_head, rbuf, sizeof(rbuf)); 583fd660bf2Stobias cvs_printf("%s:%s\n", cf->file_path, rbuf); 584fd660bf2Stobias } 585fd687d09Stobias break; 586fd660bf2Stobias } 587fd660bf2Stobias 588fd687d09Stobias if (fd1 == -1) { 589*b7041c07Sderaadt if ((fd1 = open(CVS_PATH_DEVNULL, O_RDONLY)) == -1) 590fd687d09Stobias fatal("cannot open %s", CVS_PATH_DEVNULL); 591fd687d09Stobias } 592fd687d09Stobias if (fd2 == -1) { 593*b7041c07Sderaadt if ((fd2 = open(CVS_PATH_DEVNULL, O_RDONLY)) == -1) 594fd687d09Stobias fatal("cannot open %s", CVS_PATH_DEVNULL); 595741443cbSjoris } 5963ad3fb45Sjoris 59757003866Sray if (diffreg(p1 != NULL ? cf->file_path : CVS_PATH_DEVNULL, 598219c50abSray p2 != NULL ? cf->file_path : CVS_PATH_DEVNULL, fd1, fd2, NULL, 599219c50abSray dflags) == D_ERROR) 600d71a0c9bSxsa fatal("cvs_diff_local: failed to get RCS patch"); 601d71a0c9bSxsa 6022e0d696aSjoris close(fd1); 6032e0d696aSjoris close(fd2); 6042e0d696aSjoris 6057a9e6d11Sray worklist_run(&temp_files, worklist_unlink); 60667caf486Sjoris 607397ddb8aSnicm free(p1); 608397ddb8aSnicm free(p2); 609d71a0c9bSxsa 610fd687d09Stobias cleanup: 611fd687d09Stobias if (diff_rev1 != NULL && 612fd687d09Stobias (cf->file_rcs == NULL || diff_rev1 != cf->file_rcs->rf_head) && 613fd687d09Stobias (cf->file_ent == NULL || diff_rev1 != cf->file_ent->ce_rev)) 614397ddb8aSnicm free(diff_rev1); 615fd687d09Stobias diff_rev1 = NULL; 616261cb0daSjoris 617fd687d09Stobias if (diff_rev2 != NULL && 618fd687d09Stobias (cf->file_rcs == NULL || diff_rev2 != cf->file_rcs->rf_head)) 619397ddb8aSnicm free(diff_rev2); 620fd687d09Stobias diff_rev2 = NULL; 621f9b67873Sniallo } 622