117721Speter/* 2175282Sobrien * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3175282Sobrien * 4175282Sobrien * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5175282Sobrien * and others. 6175282Sobrien * 7175282Sobrien * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8175282Sobrien * Portions Copyright (C) 1989-1992, Brian Berliner 917721Speter * 1017721Speter * You may distribute under the terms of the GNU General Public License as 1132785Speter * specified in the README file that comes with the CVS source distribution. 1217721Speter * 1317721Speter * Patch 1417721Speter * 1517721Speter * Create a Larry Wall format "patch" file between a previous release and the 1617721Speter * current head of a module, or between two releases. Can specify the 1717721Speter * release as either a date or a revision number. 18145406Ssimon * 19145406Ssimon * $FreeBSD$ 2017721Speter */ 2117721Speter 2281404Speter#include <assert.h> 2317721Speter#include "cvs.h" 2417721Speter#include "getline.h" 2517721Speter 2617721Speterstatic RETSIGTYPE patch_cleanup PROTO((void)); 27128266Speterstatic Dtype patch_dirproc PROTO ((void *callerdat, const char *dir, 28128266Speter const char *repos, const char *update_dir, 2925839Speter List *entries)); 3025839Speterstatic int patch_fileproc PROTO ((void *callerdat, struct file_info *finfo)); 3166525Speterstatic int patch_proc PROTO((int argc, char **argv, char *xwhere, 3217721Speter char *mwhere, char *mfile, int shorten, 3317721Speter int local_specified, char *mname, char *msg)); 3417721Speter 3517721Speterstatic int force_tag_match = 1; 3617721Speterstatic int patch_short = 0; 3717721Speterstatic int toptwo_diffs = 0; 3817721Speterstatic char *options = NULL; 3917721Speterstatic char *rev1 = NULL; 4025839Speterstatic int rev1_validated = 0; 4117721Speterstatic char *rev2 = NULL; 4225839Speterstatic int rev2_validated = 0; 4317721Speterstatic char *date1 = NULL; 4417721Speterstatic char *date2 = NULL; 4525839Speterstatic char *tmpfile1 = NULL; 4625839Speterstatic char *tmpfile2 = NULL; 4725839Speterstatic char *tmpfile3 = NULL; 4817721Speterstatic int unidiff = 0; 4917721Speter 5017721Speterstatic const char *const patch_usage[] = 5117721Speter{ 52175282Sobrien "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d] [-k kopt]\n", 5317721Speter " -r rev|-D date [-r rev2 | -D date2] modules...\n", 5417721Speter "\t-f\tForce a head revision match if tag/date not found.\n", 5517721Speter "\t-l\tLocal directory only, not recursive\n", 5625839Speter "\t-R\tProcess directories recursively.\n", 5717721Speter "\t-c\tContext diffs (default)\n", 5817721Speter "\t-u\tUnidiff format.\n", 5917721Speter "\t-s\tShort patch - one liner per file.\n", 6017721Speter "\t-t\tTop two diffs - last change made to the file.\n", 61175282Sobrien "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n", 62175282Sobrien "\t-k kopt\tSpecify keyword expansion mode.\n", 6317721Speter "\t-D date\tDate.\n", 6417721Speter "\t-r rev\tRevision - symbolic or numeric.\n", 6532785Speter "(Specify the --help global option for a list of other help options)\n", 6617721Speter NULL 6717721Speter}; 6817721Speter 69128266Speter 70128266Speter 7117721Speterint 7217721Speterpatch (argc, argv) 7317721Speter int argc; 7417721Speter char **argv; 7517721Speter{ 7617721Speter register int i; 77102840Speter int local = 0; 7817721Speter int c; 7917721Speter int err = 0; 8017721Speter DBM *db; 8117721Speter 8217721Speter if (argc == -1) 8317721Speter usage (patch_usage); 8417721Speter 8526065Speter optind = 0; 8625839Speter while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1) 8717721Speter { 8817721Speter switch (c) 8917721Speter { 9017721Speter case 'Q': 9117721Speter case 'q': 9217721Speter /* The CVS 1.5 client sends these options (in addition to 9317721Speter Global_option requests), so we must ignore them. */ 9417721Speter if (!server_active) 9517721Speter error (1, 0, 9617721Speter "-q or -Q must be specified before \"%s\"", 97128266Speter cvs_cmd_name); 9817721Speter break; 9917721Speter case 'f': 10017721Speter force_tag_match = 0; 10117721Speter break; 10217721Speter case 'l': 10317721Speter local = 1; 10417721Speter break; 10517721Speter case 'R': 10617721Speter local = 0; 10717721Speter break; 10817721Speter case 't': 10917721Speter toptwo_diffs = 1; 11017721Speter break; 11117721Speter case 's': 11217721Speter patch_short = 1; 11317721Speter break; 11417721Speter case 'D': 11517721Speter if (rev2 != NULL || date2 != NULL) 11617721Speter error (1, 0, 11717721Speter "no more than two revisions/dates can be specified"); 11817721Speter if (rev1 != NULL || date1 != NULL) 11917721Speter date2 = Make_Date (optarg); 12017721Speter else 12117721Speter date1 = Make_Date (optarg); 12217721Speter break; 12317721Speter case 'r': 12417721Speter if (rev2 != NULL || date2 != NULL) 12517721Speter error (1, 0, 12617721Speter "no more than two revisions/dates can be specified"); 12717721Speter if (rev1 != NULL || date1 != NULL) 12817721Speter rev2 = optarg; 12917721Speter else 13017721Speter rev1 = optarg; 13117721Speter break; 13217721Speter case 'k': 13317721Speter if (options) 13417721Speter free (options); 13517721Speter options = RCS_check_kflag (optarg); 13617721Speter break; 13717721Speter case 'V': 13825839Speter /* This option is pretty seriously broken: 13925839Speter 1. It is not clear what it does (does it change keyword 14025839Speter expansion behavior? If so, how? Or does it have 14125839Speter something to do with what version of RCS we are using? 14225839Speter Or the format we write RCS files in?). 14325839Speter 2. Because both it and -k use the options variable, 14425839Speter specifying both -V and -k doesn't work. 14525839Speter 3. At least as of CVS 1.9, it doesn't work (failed 14625839Speter assertion in RCS_checkout where it asserts that options 14725839Speter starts with -k). Few people seem to be complaining. 14825839Speter In the future (perhaps the near future), I have in mind 14925839Speter removing it entirely, and updating NEWS and cvs.texinfo, 15025839Speter but in case it is a good idea to give people more time 15125839Speter to complain if they would miss it, I'll just add this 15225839Speter quick and dirty error message for now. */ 15325839Speter error (1, 0, 15425839Speter "the -V option is obsolete and should not be used"); 15525839Speter#if 0 15617721Speter if (atoi (optarg) <= 0) 15717721Speter error (1, 0, "must specify a version number to -V"); 15817721Speter if (options) 15917721Speter free (options); 16017721Speter options = xmalloc (strlen (optarg) + 1 + 2); /* for the -V */ 16117721Speter (void) sprintf (options, "-V%s", optarg); 16225839Speter#endif 16317721Speter break; 16417721Speter case 'u': 16517721Speter unidiff = 1; /* Unidiff */ 16617721Speter break; 16717721Speter case 'c': /* Context diff */ 16817721Speter unidiff = 0; 16917721Speter break; 17017721Speter case '?': 17117721Speter default: 17217721Speter usage (patch_usage); 17317721Speter break; 17417721Speter } 17517721Speter } 17617721Speter argc -= optind; 17717721Speter argv += optind; 17817721Speter 17917721Speter /* Sanity checks */ 18017721Speter if (argc < 1) 18117721Speter usage (patch_usage); 18217721Speter 18317721Speter if (toptwo_diffs && patch_short) 18417721Speter error (1, 0, "-t and -s options are mutually exclusive"); 18517721Speter if (toptwo_diffs && (date1 != NULL || date2 != NULL || 18617721Speter rev1 != NULL || rev2 != NULL)) 18717721Speter error (1, 0, "must not specify revisions/dates with -t option!"); 18817721Speter 18917721Speter if (!toptwo_diffs && (date1 == NULL && date2 == NULL && 19017721Speter rev1 == NULL && rev2 == NULL)) 19117721Speter error (1, 0, "must specify at least one revision/date!"); 19217721Speter if (date1 != NULL && date2 != NULL) 19317721Speter if (RCS_datecmp (date1, date2) >= 0) 19417721Speter error (1, 0, "second date must come after first date!"); 19517721Speter 19617721Speter /* if options is NULL, make it a NULL string */ 19717721Speter if (options == NULL) 19817721Speter options = xstrdup (""); 19917721Speter 20017721Speter#ifdef CLIENT_SUPPORT 20181404Speter if (current_parsed_root->isremote) 20217721Speter { 20317721Speter /* We're the client side. Fire up the remote server. */ 20417721Speter start_server (); 20517721Speter 20617721Speter ign_setup (); 20717721Speter 20817721Speter if (local) 20917721Speter send_arg("-l"); 21025839Speter if (!force_tag_match) 21117721Speter send_arg("-f"); 21217721Speter if (toptwo_diffs) 21317721Speter send_arg("-t"); 21417721Speter if (patch_short) 21517721Speter send_arg("-s"); 21617721Speter if (unidiff) 21717721Speter send_arg("-u"); 21817721Speter 21917721Speter if (rev1) 22017721Speter option_with_arg ("-r", rev1); 22117721Speter if (date1) 22217721Speter client_senddate (date1); 22317721Speter if (rev2) 22417721Speter option_with_arg ("-r", rev2); 22517721Speter if (date2) 22617721Speter client_senddate (date2); 22717721Speter if (options[0] != '\0') 22817721Speter send_arg (options); 22917721Speter 23017721Speter { 23117721Speter int i; 23217721Speter for (i = 0; i < argc; ++i) 23317721Speter send_arg (argv[i]); 23417721Speter } 23517721Speter 23617721Speter send_to_server ("rdiff\012", 0); 23717721Speter return get_responses_and_close (); 23817721Speter } 23917721Speter#endif 24017721Speter 24117721Speter /* clean up if we get a signal */ 24266525Speter#ifdef SIGABRT 243128266Speter (void)SIG_register (SIGABRT, patch_cleanup); 24466525Speter#endif 24517721Speter#ifdef SIGHUP 246128266Speter (void)SIG_register (SIGHUP, patch_cleanup); 24717721Speter#endif 24817721Speter#ifdef SIGINT 249128266Speter (void)SIG_register (SIGINT, patch_cleanup); 25017721Speter#endif 25117721Speter#ifdef SIGQUIT 252128266Speter (void)SIG_register (SIGQUIT, patch_cleanup); 25317721Speter#endif 25417721Speter#ifdef SIGPIPE 255128266Speter (void)SIG_register (SIGPIPE, patch_cleanup); 25617721Speter#endif 25717721Speter#ifdef SIGTERM 258128266Speter (void)SIG_register (SIGTERM, patch_cleanup); 25917721Speter#endif 26017721Speter 26117721Speter db = open_module (); 26217721Speter for (i = 0; i < argc; i++) 26317721Speter err += do_module (db, argv[i], PATCH, "Patching", patch_proc, 264128266Speter (char *)NULL, 0, local, 0, 0, (char *)NULL); 26517721Speter close_module (db); 26617721Speter free (options); 26717721Speter patch_cleanup (); 268128266Speter return err; 26917721Speter} 27017721Speter 271128266Speter 272128266Speter 27317721Speter/* 27417721Speter * callback proc for doing the real work of patching 27517721Speter */ 27617721Speter/* ARGSUSED */ 27717721Speterstatic int 27866525Speterpatch_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified, 27917721Speter mname, msg) 28066525Speter int argc; 28117721Speter char **argv; 28217721Speter char *xwhere; 28317721Speter char *mwhere; 28417721Speter char *mfile; 28517721Speter int shorten; 28617721Speter int local_specified; 28717721Speter char *mname; 28817721Speter char *msg; 28917721Speter{ 29066525Speter char *myargv[2]; 29117721Speter int err = 0; 29217721Speter int which; 29325839Speter char *repository; 29425839Speter char *where; 29517721Speter 296128266Speter repository = xmalloc (strlen (current_parsed_root->directory) 297128266Speter + strlen (argv[0]) 29881404Speter + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2); 299128266Speter (void)sprintf (repository, "%s/%s", 300128266Speter current_parsed_root->directory, argv[0]); 30181404Speter where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1) 30281404Speter + 1); 303128266Speter (void)strcpy (where, argv[0]); 30417721Speter 30517721Speter /* if mfile isn't null, we need to set up to do only part of the module */ 30617721Speter if (mfile != NULL) 30717721Speter { 30817721Speter char *cp; 30925839Speter char *path; 31017721Speter 31117721Speter /* if the portion of the module is a path, put the dir part on repos */ 31217721Speter if ((cp = strrchr (mfile, '/')) != NULL) 31317721Speter { 31417721Speter *cp = '\0'; 315128266Speter (void)strcat (repository, "/"); 316128266Speter (void)strcat (repository, mfile); 317128266Speter (void)strcat (where, "/"); 318128266Speter (void)strcat (where, mfile); 31917721Speter mfile = cp + 1; 32017721Speter } 32117721Speter 32217721Speter /* take care of the rest */ 32381404Speter path = xmalloc (strlen (repository) + strlen (mfile) + 2); 324128266Speter (void)sprintf (path, "%s/%s", repository, mfile); 32517721Speter if (isdir (path)) 32617721Speter { 32717721Speter /* directory means repository gets the dir tacked on */ 328128266Speter (void)strcpy (repository, path); 329128266Speter (void)strcat (where, "/"); 330128266Speter (void)strcat (where, mfile); 33117721Speter } 33217721Speter else 33317721Speter { 33466525Speter myargv[0] = argv[0]; 33566525Speter myargv[1] = mfile; 33666525Speter argc = 2; 33766525Speter argv = myargv; 33817721Speter } 33925839Speter free (path); 34017721Speter } 34117721Speter 34217721Speter /* cd to the starting repository */ 34325839Speter if ( CVS_CHDIR (repository) < 0) 34417721Speter { 34517721Speter error (0, errno, "cannot chdir to %s", repository); 34625839Speter free (repository); 347175282Sobrien free (where); 348128266Speter return 1; 34917721Speter } 35017721Speter 35117721Speter if (force_tag_match) 35217721Speter which = W_REPOS | W_ATTIC; 35317721Speter else 35417721Speter which = W_REPOS; 35517721Speter 35617721Speter if (rev1 != NULL && !rev1_validated) 35717721Speter { 358128266Speter tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0, 359128266Speter repository); 36017721Speter rev1_validated = 1; 36117721Speter } 36217721Speter if (rev2 != NULL && !rev2_validated) 36317721Speter { 364128266Speter tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0, 365128266Speter repository); 36617721Speter rev2_validated = 1; 36717721Speter } 36817721Speter 36917721Speter /* start the recursion processor */ 370128266Speter err = start_recursion (patch_fileproc, (FILESDONEPROC)NULL, patch_dirproc, 371128266Speter (DIRLEAVEPROC)NULL, NULL, 372102840Speter argc - 1, argv + 1, local_specified, 373128266Speter which, 0, CVS_LOCK_READ, where, 1, repository); 374128266Speter free (repository); 37525839Speter free (where); 37617721Speter 377128266Speter return err; 37817721Speter} 37917721Speter 380128266Speter 381128266Speter 38217721Speter/* 38317721Speter * Called to examine a particular RCS file, as appropriate with the options 38417721Speter * that were set above. 38517721Speter */ 38617721Speter/* ARGSUSED */ 38717721Speterstatic int 38825839Speterpatch_fileproc (callerdat, finfo) 38925839Speter void *callerdat; 39017721Speter struct file_info *finfo; 39117721Speter{ 39217721Speter struct utimbuf t; 39317721Speter char *vers_tag, *vers_head; 39425839Speter char *rcs = NULL; 395145406Ssimon char *rcs_orig = NULL; 39617721Speter RCSNode *rcsfile; 39717721Speter FILE *fp1, *fp2, *fp3; 39817721Speter int ret = 0; 39917721Speter int isattic = 0; 40017721Speter int retcode = 0; 40125839Speter char *file1; 40225839Speter char *file2; 40325839Speter char *strippath; 40417721Speter char *line1, *line2; 40517721Speter size_t line1_chars_allocated; 40617721Speter size_t line2_chars_allocated; 40717721Speter char *cp1, *cp2; 40817721Speter FILE *fp; 40932785Speter int line_length; 410175282Sobrien int dargc = 0; 411175282Sobrien size_t darg_allocated = 0; 412175282Sobrien char **dargv = NULL; 41317721Speter 41425839Speter line1 = NULL; 41525839Speter line1_chars_allocated = 0; 41625839Speter line2 = NULL; 41725839Speter line2_chars_allocated = 0; 418128266Speter vers_tag = vers_head = NULL; 41925839Speter 42017721Speter /* find the parsed rcs file */ 42117721Speter if ((rcsfile = finfo->rcs) == NULL) 42225839Speter { 42325839Speter ret = 1; 42425839Speter goto out2; 42525839Speter } 42617721Speter if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC)) 42717721Speter isattic = 1; 42817721Speter 429145406Ssimon rcs_orig = rcs = xmalloc (strlen (finfo->file) + sizeof (RCSEXT) + 5); 43017721Speter (void) sprintf (rcs, "%s%s", finfo->file, RCSEXT); 43117721Speter 43217721Speter /* if vers_head is NULL, may have been removed from the release */ 43317721Speter if (isattic && rev2 == NULL && date2 == NULL) 43417721Speter vers_head = NULL; 43517721Speter else 43625839Speter { 43725839Speter vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match, 43825839Speter (int *) NULL); 43925839Speter if (vers_head != NULL && RCS_isdead (rcsfile, vers_head)) 44025839Speter { 44125839Speter free (vers_head); 44225839Speter vers_head = NULL; 44325839Speter } 44425839Speter } 44517721Speter 44617721Speter if (toptwo_diffs) 44717721Speter { 44817721Speter if (vers_head == NULL) 44925839Speter { 45025839Speter ret = 1; 45125839Speter goto out2; 45225839Speter } 45317721Speter 45417721Speter if (!date1) 45525839Speter date1 = xmalloc (MAXDATELEN); 45617721Speter *date1 = '\0'; 457102840Speter if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == (time_t)-1) 45817721Speter { 45917721Speter if (!really_quiet) 46017721Speter error (0, 0, "cannot find date in rcs file %s revision %s", 46117721Speter rcs, vers_head); 46225839Speter ret = 1; 46325839Speter goto out2; 46417721Speter } 46517721Speter } 46625839Speter vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, 46725839Speter (int *) NULL); 46825839Speter if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag)) 46925839Speter { 47025839Speter free (vers_tag); 47125839Speter vers_tag = NULL; 47225839Speter } 47317721Speter 474128266Speter if ((vers_tag == NULL && vers_head == NULL) || 475128266Speter (vers_tag != NULL && vers_head != NULL && 476128266Speter strcmp (vers_head, vers_tag) == 0)) 47725839Speter { 478128266Speter /* Nothing known about specified revs or 479128266Speter * not changed between releases. 480128266Speter */ 48125839Speter ret = 0; 48225839Speter goto out2; 48325839Speter } 48417721Speter 485128266Speter if( patch_short && ( vers_tag == NULL || vers_head == NULL ) ) 48625839Speter { 487128266Speter /* For adds & removes with a short patch requested, we can print our 488128266Speter * error message now and get out. 489128266Speter */ 49032785Speter cvs_output ("File ", 0); 49132785Speter cvs_output (finfo->fullname, 0); 49217721Speter if (vers_tag == NULL) 49332785Speter { 494128266Speter cvs_output( " is new; ", 0 ); 495128266Speter cvs_output( rev2 ? rev2 : date2 ? date2 : "current", 0 ); 496128266Speter cvs_output( " revision ", 0 ); 49732785Speter cvs_output (vers_head, 0); 49832785Speter cvs_output ("\n", 1); 49932785Speter } 50017721Speter else 50132785Speter { 502128266Speter cvs_output( " is removed; ", 0 ); 503128266Speter cvs_output( rev1 ? rev1 : date1, 0 ); 504128266Speter cvs_output( " revision ", 0 ); 505128266Speter cvs_output( vers_tag, 0 ); 50632785Speter cvs_output ("\n", 1); 50732785Speter } 50825839Speter ret = 0; 50925839Speter goto out2; 51017721Speter } 51134461Speter 51234461Speter /* Create 3 empty files. I'm not really sure there is any advantage 51381404Speter * to doing so now rather than just waiting until later. 51481404Speter * 51581404Speter * There is - cvs_temp_file opens the file so that it can guarantee that 51681404Speter * we have exclusive write access to the file. Unfortunately we spoil that 51781404Speter * by closing it and reopening it again. Of course any better solution 51881404Speter * requires that the RCS functions accept open file pointers rather than 51981404Speter * simple file names. 52081404Speter */ 52181404Speter if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL) 52234461Speter { 523175282Sobrien error (0, errno, "cannot create temporary file %s", 524175282Sobrien tmpfile1 ? tmpfile1 : "(null)"); 52534461Speter ret = 1; 52634461Speter goto out; 52734461Speter } 52834461Speter else 52934461Speter if (fclose (fp1) < 0) 53034461Speter error (0, errno, "warning: cannot close %s", tmpfile1); 53181404Speter if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL) 53234461Speter { 533175282Sobrien error (0, errno, "cannot create temporary file %s", 534175282Sobrien tmpfile2 ? tmpfile2 : "(null)"); 53534461Speter ret = 1; 53634461Speter goto out; 53734461Speter } 53834461Speter else 53934461Speter if (fclose (fp2) < 0) 54034461Speter error (0, errno, "warning: cannot close %s", tmpfile2); 54181404Speter if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL) 54217721Speter { 543175282Sobrien error (0, errno, "cannot create temporary file %s", 544175282Sobrien tmpfile3 ? tmpfile3 : "(null)"); 54517721Speter ret = 1; 54617721Speter goto out; 54717721Speter } 54834461Speter else 54934461Speter if (fclose (fp3) < 0) 55034461Speter error (0, errno, "warning: cannot close %s", tmpfile3); 55134461Speter 55217721Speter if (vers_tag != NULL) 55317721Speter { 554128266Speter retcode = RCS_checkout (rcsfile, (char *)NULL, vers_tag, 55525839Speter rev1, options, tmpfile1, 556128266Speter (RCSCHECKOUTPROC)NULL, (void *)NULL); 55717721Speter if (retcode != 0) 55817721Speter { 55934461Speter error (0, 0, 56034461Speter "cannot check out revision %s of %s", vers_tag, rcs); 56117721Speter ret = 1; 56217721Speter goto out; 56317721Speter } 56417721Speter memset ((char *) &t, 0, sizeof (t)); 56517721Speter if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag, 56617721Speter (char *) 0, 0)) != -1) 56726801Speter /* I believe this timestamp only affects the dates in our diffs, 56826801Speter and therefore should be on the server, not the client. */ 56926801Speter (void) utime (tmpfile1, &t); 57017721Speter } 57117721Speter else if (toptwo_diffs) 57217721Speter { 57317721Speter ret = 1; 57417721Speter goto out; 57517721Speter } 57617721Speter if (vers_head != NULL) 57717721Speter { 578128266Speter retcode = RCS_checkout (rcsfile, (char *)NULL, vers_head, 57925839Speter rev2, options, tmpfile2, 580128266Speter (RCSCHECKOUTPROC)NULL, (void *)NULL); 58117721Speter if (retcode != 0) 58217721Speter { 58334461Speter error (0, 0, 58434461Speter "cannot check out revision %s of %s", vers_head, rcs); 58517721Speter ret = 1; 58617721Speter goto out; 58717721Speter } 58817721Speter if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head, 589128266Speter (char *)0, 0)) != -1) 59026801Speter /* I believe this timestamp only affects the dates in our diffs, 59126801Speter and therefore should be on the server, not the client. */ 592128266Speter (void)utime (tmpfile2, &t); 59317721Speter } 59417721Speter 595175282Sobrien if (unidiff) run_add_arg_p (&dargc, &darg_allocated, &dargv, "-u"); 596175282Sobrien else run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c"); 597175282Sobrien switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, dargc, dargv, 598175282Sobrien tmpfile3)) 59917721Speter { 60017721Speter case -1: /* fork/wait failure */ 60117721Speter error (1, errno, "fork for diff failed on %s", rcs); 60217721Speter break; 60317721Speter case 0: /* nothing to do */ 60417721Speter break; 60517721Speter case 1: 60617721Speter /* 60717721Speter * The two revisions are really different, so read the first two 60817721Speter * lines of the diff output file, and munge them to include more 609128266Speter * reasonable file names that "patch" will understand, unless the 610128266Speter * user wanted a short patch. In that case, just output the short 611128266Speter * message. 61217721Speter */ 613128266Speter if( patch_short ) 614128266Speter { 615128266Speter cvs_output ("File ", 0); 616128266Speter cvs_output (finfo->fullname, 0); 617128266Speter cvs_output (" changed from revision ", 0); 618128266Speter cvs_output (vers_tag, 0); 619128266Speter cvs_output (" to ", 0); 620128266Speter cvs_output (vers_head, 0); 621128266Speter cvs_output ("\n", 1); 622128266Speter ret = 0; 623128266Speter goto out; 624128266Speter } 62517721Speter 62617721Speter /* Output an "Index:" line for patch to use */ 62732785Speter cvs_output ("Index: ", 0); 62832785Speter cvs_output (finfo->fullname, 0); 62932785Speter cvs_output ("\n", 1); 63017721Speter 631128266Speter /* Now the munging. */ 63217721Speter fp = open_file (tmpfile3, "r"); 63317721Speter if (getline (&line1, &line1_chars_allocated, fp) < 0 || 63417721Speter getline (&line2, &line2_chars_allocated, fp) < 0) 63517721Speter { 63634461Speter if (feof (fp)) 63734461Speter error (0, 0, "\ 63834461Speterfailed to read diff file header %s for %s: end of file", tmpfile3, rcs); 63934461Speter else 64034461Speter error (0, errno, 64134461Speter "failed to read diff file header %s for %s", 64234461Speter tmpfile3, rcs); 64317721Speter ret = 1; 64434461Speter if (fclose (fp) < 0) 64534461Speter error (0, errno, "error closing %s", tmpfile3); 64617721Speter goto out; 64717721Speter } 64817721Speter if (!unidiff) 64917721Speter { 65017721Speter if (strncmp (line1, "*** ", 4) != 0 || 65117721Speter strncmp (line2, "--- ", 4) != 0 || 65217721Speter (cp1 = strchr (line1, '\t')) == NULL || 65317721Speter (cp2 = strchr (line2, '\t')) == NULL) 65417721Speter { 65517721Speter error (0, 0, "invalid diff header for %s", rcs); 65617721Speter ret = 1; 65734461Speter if (fclose (fp) < 0) 65834461Speter error (0, errno, "error closing %s", tmpfile3); 65917721Speter goto out; 66017721Speter } 66117721Speter } 66217721Speter else 66317721Speter { 66417721Speter if (strncmp (line1, "--- ", 4) != 0 || 66517721Speter strncmp (line2, "+++ ", 4) != 0 || 66617721Speter (cp1 = strchr (line1, '\t')) == NULL || 66717721Speter (cp2 = strchr (line2, '\t')) == NULL) 66817721Speter { 66917721Speter error (0, 0, "invalid unidiff header for %s", rcs); 67017721Speter ret = 1; 67134461Speter if (fclose (fp) < 0) 67234461Speter error (0, errno, "error closing %s", tmpfile3); 67317721Speter goto out; 67417721Speter } 67517721Speter } 67681404Speter assert (current_parsed_root != NULL); 67781404Speter assert (current_parsed_root->directory != NULL); 67825839Speter { 679128266Speter strippath = xmalloc (strlen (current_parsed_root->directory) 680128266Speter + 2); 681128266Speter (void)sprintf (strippath, "%s/", 682128266Speter current_parsed_root->directory); 68325839Speter } 68481404Speter /*else 68581404Speter strippath = xstrdup (REPOS_STRIP); */ 68617721Speter if (strncmp (rcs, strippath, strlen (strippath)) == 0) 68717721Speter rcs += strlen (strippath); 68825839Speter free (strippath); 68917721Speter if (vers_tag != NULL) 69017721Speter { 69125839Speter file1 = xmalloc (strlen (finfo->fullname) 69225839Speter + strlen (vers_tag) 69325839Speter + 10); 694128266Speter (void)sprintf (file1, "%s:%s", finfo->fullname, vers_tag); 69517721Speter } 69617721Speter else 69717721Speter { 69825839Speter file1 = xstrdup (DEVNULL); 69917721Speter } 70025839Speter file2 = xmalloc (strlen (finfo->fullname) 70125839Speter + (vers_head != NULL ? strlen (vers_head) : 10) 70225839Speter + 10); 703128266Speter (void)sprintf (file2, "%s:%s", finfo->fullname, 704128266Speter vers_head ? vers_head : "removed"); 70517721Speter 70632785Speter /* Note that the string "diff" is specified by POSIX (for -c) 70732785Speter and is part of the diff output format, not the name of a 70832785Speter program. */ 70917721Speter if (unidiff) 71017721Speter { 71132785Speter cvs_output ("diff -u ", 0); 71232785Speter cvs_output (file1, 0); 71332785Speter cvs_output (" ", 1); 71432785Speter cvs_output (file2, 0); 71532785Speter cvs_output ("\n", 1); 71632785Speter 71732785Speter cvs_output ("--- ", 0); 71832785Speter cvs_output (file1, 0); 71932785Speter cvs_output (cp1, 0); 72032785Speter cvs_output ("+++ ", 0); 72117721Speter } 72217721Speter else 72317721Speter { 72432785Speter cvs_output ("diff -c ", 0); 72532785Speter cvs_output (file1, 0); 72632785Speter cvs_output (" ", 1); 72732785Speter cvs_output (file2, 0); 72832785Speter cvs_output ("\n", 1); 72932785Speter 73032785Speter cvs_output ("*** ", 0); 73132785Speter cvs_output (file1, 0); 73232785Speter cvs_output (cp1, 0); 73332785Speter cvs_output ("--- ", 0); 73417721Speter } 73517721Speter 73632785Speter cvs_output (finfo->fullname, 0); 73732785Speter cvs_output (cp2, 0); 73832785Speter 73917721Speter /* spew the rest of the diff out */ 74032785Speter while ((line_length 74132785Speter = getline (&line1, &line1_chars_allocated, fp)) 74232785Speter >= 0) 74332785Speter cvs_output (line1, 0); 74432785Speter if (line_length < 0 && !feof (fp)) 74532785Speter error (0, errno, "cannot read %s", tmpfile3); 74632785Speter 74732785Speter if (fclose (fp) < 0) 74832785Speter error (0, errno, "cannot close %s", tmpfile3); 74925839Speter free (file1); 75025839Speter free (file2); 75117721Speter break; 75217721Speter default: 75317721Speter error (0, 0, "diff failed for %s", finfo->fullname); 75417721Speter } 75517721Speter out: 75617721Speter if (line1) 75717721Speter free (line1); 75817721Speter if (line2) 75917721Speter free (line2); 760175282Sobrien if (tmpfile1 != NULL) 761175282Sobrien { 762175282Sobrien if (CVS_UNLINK (tmpfile1) < 0) 763175282Sobrien error (0, errno, "cannot unlink %s", tmpfile1); 764175282Sobrien free (tmpfile1); 765175282Sobrien tmpfile1 = NULL; 766175282Sobrien } 767175282Sobrien if (tmpfile2 != NULL) 768175282Sobrien { 769175282Sobrien if (CVS_UNLINK (tmpfile2) < 0) 770175282Sobrien error (0, errno, "cannot unlink %s", tmpfile2); 771175282Sobrien free (tmpfile2); 772175282Sobrien tmpfile2 = NULL; 773175282Sobrien } 774175282Sobrien if (tmpfile3 != NULL) 775175282Sobrien { 776175282Sobrien if (CVS_UNLINK (tmpfile3) < 0) 777175282Sobrien error (0, errno, "cannot unlink %s", tmpfile3); 778175282Sobrien free (tmpfile3); 779175282Sobrien tmpfile3 = NULL; 780175282Sobrien } 78125839Speter 782175282Sobrien if (dargc) 783175282Sobrien { 784175282Sobrien run_arg_free_p (dargc, dargv); 785175282Sobrien free (dargv); 786175282Sobrien } 787175282Sobrien 78825839Speter out2: 78966525Speter if (vers_tag != NULL) 79066525Speter free (vers_tag); 79166525Speter if (vers_head != NULL) 79266525Speter free (vers_head); 793145406Ssimon if (rcs_orig) 794145406Ssimon free (rcs_orig); 795128266Speter return ret; 79617721Speter} 79717721Speter 798128266Speter 799128266Speter 80017721Speter/* 80117721Speter * Print a warm fuzzy message 80217721Speter */ 80317721Speter/* ARGSUSED */ 80417721Speterstatic Dtype 80525839Speterpatch_dirproc (callerdat, dir, repos, update_dir, entries) 80625839Speter void *callerdat; 807128266Speter const char *dir; 808128266Speter const char *repos; 809128266Speter const char *update_dir; 81025839Speter List *entries; 81117721Speter{ 81217721Speter if (!quiet) 81317721Speter error (0, 0, "Diffing %s", update_dir); 81417721Speter return (R_PROCESS); 81517721Speter} 81617721Speter 81717721Speter/* 81817721Speter * Clean up temporary files 81917721Speter */ 82017721Speterstatic RETSIGTYPE 82117721Speterpatch_cleanup () 82217721Speter{ 82354427Speter /* Note that the checks for existence_error are because we are 82454427Speter called from a signal handler, without SIG_begincrsect, so 82554427Speter we don't know whether the files got created. */ 82654427Speter 82725839Speter if (tmpfile1 != NULL) 82825839Speter { 82954427Speter if (unlink_file (tmpfile1) < 0 83054427Speter && !existence_error (errno)) 83154427Speter error (0, errno, "cannot remove %s", tmpfile1); 83225839Speter free (tmpfile1); 83325839Speter } 83425839Speter if (tmpfile2 != NULL) 83525839Speter { 83654427Speter if (unlink_file (tmpfile2) < 0 83754427Speter && !existence_error (errno)) 83854427Speter error (0, errno, "cannot remove %s", tmpfile2); 83925839Speter free (tmpfile2); 84025839Speter } 84125839Speter if (tmpfile3 != NULL) 84225839Speter { 84354427Speter if (unlink_file (tmpfile3) < 0 84454427Speter && !existence_error (errno)) 84554427Speter error (0, errno, "cannot remove %s", tmpfile3); 84625839Speter free (tmpfile3); 84725839Speter } 84825839Speter tmpfile1 = tmpfile2 = tmpfile3 = NULL; 84917721Speter} 850