"Fossies" - the Fresh Open Source Software Archive

Member "cvs-1.11.23/src/patch.c" (7 May 2008, 21940 Bytes) of package /linux/misc/old/cvs-1.11.23.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "patch.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
    3  *
    4  * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
    5  *                                  and others.
    6  *
    7  * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
    8  * Portions Copyright (C) 1989-1992, Brian Berliner
    9  * 
   10  * You may distribute under the terms of the GNU General Public License as
   11  * specified in the README file that comes with the CVS source distribution.
   12  * 
   13  * Patch
   14  * 
   15  * Create a Larry Wall format "patch" file between a previous release and the
   16  * current head of a module, or between two releases.  Can specify the
   17  * release as either a date or a revision number.
   18  */
   19 
   20 #include <assert.h>
   21 #include "cvs.h"
   22 #include "getline.h"
   23 
   24 static RETSIGTYPE patch_cleanup PROTO((void));
   25 static Dtype patch_dirproc PROTO ((void *callerdat, const char *dir,
   26                    const char *repos, const char *update_dir,
   27                    List *entries));
   28 static int patch_fileproc PROTO ((void *callerdat, struct file_info *finfo));
   29 static int patch_proc PROTO((int argc, char **argv, char *xwhere,
   30                char *mwhere, char *mfile, int shorten,
   31                int local_specified, char *mname, char *msg));
   32 
   33 static int force_tag_match = 1;
   34 static int patch_short = 0;
   35 static int toptwo_diffs = 0;
   36 static char *options = NULL;
   37 static char *rev1 = NULL;
   38 static int rev1_validated = 0;
   39 static char *rev2 = NULL;
   40 static int rev2_validated = 0;
   41 static char *date1 = NULL;
   42 static char *date2 = NULL;
   43 static char *tmpfile1 = NULL;
   44 static char *tmpfile2 = NULL;
   45 static char *tmpfile3 = NULL;
   46 static int unidiff = 0;
   47 
   48 static const char *const patch_usage[] =
   49 {
   50     "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d] [-k kopt]\n",
   51     "    -r rev|-D date [-r rev2 | -D date2] modules...\n",
   52     "\t-f\tForce a head revision match if tag/date not found.\n",
   53     "\t-l\tLocal directory only, not recursive\n",
   54     "\t-R\tProcess directories recursively.\n",
   55     "\t-c\tContext diffs (default)\n",
   56     "\t-u\tUnidiff format.\n",
   57     "\t-s\tShort patch - one liner per file.\n",
   58     "\t-t\tTop two diffs - last change made to the file.\n",
   59     "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
   60     "\t-k kopt\tSpecify keyword expansion mode.\n",
   61     "\t-D date\tDate.\n",
   62     "\t-r rev\tRevision - symbolic or numeric.\n",
   63     "(Specify the --help global option for a list of other help options)\n",
   64     NULL
   65 };
   66 
   67 
   68 
   69 int
   70 patch (argc, argv)
   71     int argc;
   72     char **argv;
   73 {
   74     register int i;
   75     int local = 0;
   76     int c;
   77     int err = 0;
   78     DBM *db;
   79 
   80     if (argc == -1)
   81     usage (patch_usage);
   82 
   83     optind = 0;
   84     while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1)
   85     {
   86     switch (c)
   87     {
   88         case 'Q':
   89         case 'q':
   90         /* The CVS 1.5 client sends these options (in addition to
   91            Global_option requests), so we must ignore them.  */
   92         if (!server_active)
   93             error (1, 0,
   94                "-q or -Q must be specified before \"%s\"",
   95                cvs_cmd_name);
   96         break;
   97         case 'f':
   98         force_tag_match = 0;
   99         break;
  100         case 'l':
  101         local = 1;
  102         break;
  103         case 'R':
  104         local = 0;
  105         break;
  106         case 't':
  107         toptwo_diffs = 1;
  108         break;
  109         case 's':
  110         patch_short = 1;
  111         break;
  112         case 'D':
  113         if (rev2 != NULL || date2 != NULL)
  114             error (1, 0,
  115                "no more than two revisions/dates can be specified");
  116         if (rev1 != NULL || date1 != NULL)
  117             date2 = Make_Date (optarg);
  118         else
  119             date1 = Make_Date (optarg);
  120         break;
  121         case 'r':
  122         if (rev2 != NULL || date2 != NULL)
  123             error (1, 0,
  124                "no more than two revisions/dates can be specified");
  125         if (rev1 != NULL || date1 != NULL)
  126             rev2 = optarg;
  127         else
  128             rev1 = optarg;
  129         break;
  130         case 'k':
  131         if (options)
  132             free (options);
  133         options = RCS_check_kflag (optarg);
  134         break;
  135         case 'V':
  136         /* This option is pretty seriously broken:
  137            1.  It is not clear what it does (does it change keyword
  138            expansion behavior?  If so, how?  Or does it have
  139            something to do with what version of RCS we are using?
  140            Or the format we write RCS files in?).
  141            2.  Because both it and -k use the options variable,
  142            specifying both -V and -k doesn't work.
  143            3.  At least as of CVS 1.9, it doesn't work (failed
  144            assertion in RCS_checkout where it asserts that options
  145            starts with -k).  Few people seem to be complaining.
  146            In the future (perhaps the near future), I have in mind
  147            removing it entirely, and updating NEWS and cvs.texinfo,
  148            but in case it is a good idea to give people more time
  149            to complain if they would miss it, I'll just add this
  150            quick and dirty error message for now.  */
  151         error (1, 0,
  152                "the -V option is obsolete and should not be used");
  153 #if 0
  154         if (atoi (optarg) <= 0)
  155             error (1, 0, "must specify a version number to -V");
  156         if (options)
  157             free (options);
  158         options = xmalloc (strlen (optarg) + 1 + 2);    /* for the -V */
  159         (void) sprintf (options, "-V%s", optarg);
  160 #endif
  161         break;
  162         case 'u':
  163         unidiff = 1;        /* Unidiff */
  164         break;
  165         case 'c':           /* Context diff */
  166         unidiff = 0;
  167         break;
  168         case '?':
  169         default:
  170         usage (patch_usage);
  171         break;
  172     }
  173     }
  174     argc -= optind;
  175     argv += optind;
  176 
  177     /* Sanity checks */
  178     if (argc < 1)
  179     usage (patch_usage);
  180 
  181     if (toptwo_diffs && patch_short)
  182     error (1, 0, "-t and -s options are mutually exclusive");
  183     if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
  184              rev1 != NULL || rev2 != NULL))
  185     error (1, 0, "must not specify revisions/dates with -t option!");
  186 
  187     if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
  188               rev1 == NULL && rev2 == NULL))
  189     error (1, 0, "must specify at least one revision/date!");
  190     if (date1 != NULL && date2 != NULL)
  191     if (RCS_datecmp (date1, date2) >= 0)
  192         error (1, 0, "second date must come after first date!");
  193 
  194     /* if options is NULL, make it a NULL string */
  195     if (options == NULL)
  196     options = xstrdup ("");
  197 
  198 #ifdef CLIENT_SUPPORT
  199     if (current_parsed_root->isremote)
  200     {
  201     /* We're the client side.  Fire up the remote server.  */
  202     start_server ();
  203     
  204     ign_setup ();
  205 
  206     if (local)
  207         send_arg("-l");
  208     if (!force_tag_match)
  209         send_arg("-f");
  210     if (toptwo_diffs)
  211         send_arg("-t");
  212     if (patch_short)
  213         send_arg("-s");
  214     if (unidiff)
  215         send_arg("-u");
  216 
  217     if (rev1)
  218         option_with_arg ("-r", rev1);
  219     if (date1)
  220         client_senddate (date1);
  221     if (rev2)
  222         option_with_arg ("-r", rev2);
  223     if (date2)
  224         client_senddate (date2);
  225     if (options[0] != '\0')
  226         send_arg (options);
  227 
  228     {
  229         int i;
  230         for (i = 0; i < argc; ++i)
  231         send_arg (argv[i]);
  232     }
  233 
  234     send_to_server ("rdiff\012", 0);
  235         return get_responses_and_close ();
  236     }
  237 #endif
  238 
  239     /* clean up if we get a signal */
  240 #ifdef SIGABRT
  241     (void)SIG_register (SIGABRT, patch_cleanup);
  242 #endif
  243 #ifdef SIGHUP
  244     (void)SIG_register (SIGHUP, patch_cleanup);
  245 #endif
  246 #ifdef SIGINT
  247     (void)SIG_register (SIGINT, patch_cleanup);
  248 #endif
  249 #ifdef SIGQUIT
  250     (void)SIG_register (SIGQUIT, patch_cleanup);
  251 #endif
  252 #ifdef SIGPIPE
  253     (void)SIG_register (SIGPIPE, patch_cleanup);
  254 #endif
  255 #ifdef SIGTERM
  256     (void)SIG_register (SIGTERM, patch_cleanup);
  257 #endif
  258 
  259     db = open_module ();
  260     for (i = 0; i < argc; i++)
  261     err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
  262               (char *)NULL, 0, local, 0, 0, (char *)NULL);
  263     close_module (db);
  264     free (options);
  265     patch_cleanup ();
  266     return err;
  267 }
  268 
  269 
  270 
  271 /*
  272  * callback proc for doing the real work of patching
  273  */
  274 /* ARGSUSED */
  275 static int
  276 patch_proc (argc, argv, xwhere, mwhere, mfile, shorten, local_specified,
  277         mname, msg)
  278     int argc;
  279     char **argv;
  280     char *xwhere;
  281     char *mwhere;
  282     char *mfile;
  283     int shorten;
  284     int local_specified;
  285     char *mname;
  286     char *msg;
  287 {
  288     char *myargv[2];
  289     int err = 0;
  290     int which;
  291     char *repository;
  292     char *where;
  293 
  294     repository = xmalloc (strlen (current_parsed_root->directory)
  295                           + strlen (argv[0])
  296               + (mfile == NULL ? 0 : strlen (mfile) + 1) + 2);
  297     (void)sprintf (repository, "%s/%s",
  298                    current_parsed_root->directory, argv[0]);
  299     where = xmalloc (strlen (argv[0]) + (mfile == NULL ? 0 : strlen (mfile) + 1)
  300              + 1);
  301     (void)strcpy (where, argv[0]);
  302 
  303     /* if mfile isn't null, we need to set up to do only part of the module */
  304     if (mfile != NULL)
  305     {
  306     char *cp;
  307     char *path;
  308 
  309     /* if the portion of the module is a path, put the dir part on repos */
  310     if ((cp = strrchr (mfile, '/')) != NULL)
  311     {
  312         *cp = '\0';
  313         (void)strcat (repository, "/");
  314         (void)strcat (repository, mfile);
  315         (void)strcat (where, "/");
  316         (void)strcat (where, mfile);
  317         mfile = cp + 1;
  318     }
  319 
  320     /* take care of the rest */
  321     path = xmalloc (strlen (repository) + strlen (mfile) + 2);
  322     (void)sprintf (path, "%s/%s", repository, mfile);
  323     if (isdir (path))
  324     {
  325         /* directory means repository gets the dir tacked on */
  326         (void)strcpy (repository, path);
  327         (void)strcat (where, "/");
  328         (void)strcat (where, mfile);
  329     }
  330     else
  331     {
  332         myargv[0] = argv[0];
  333         myargv[1] = mfile;
  334         argc = 2;
  335         argv = myargv;
  336     }
  337     free (path);
  338     }
  339 
  340     /* cd to the starting repository */
  341     if ( CVS_CHDIR (repository) < 0)
  342     {
  343     error (0, errno, "cannot chdir to %s", repository);
  344     free (repository);
  345     free (where);
  346     return 1;
  347     }
  348 
  349     if (force_tag_match)
  350     which = W_REPOS | W_ATTIC;
  351     else
  352     which = W_REPOS;
  353 
  354     if (rev1 != NULL && !rev1_validated)
  355     {
  356     tag_check_valid (rev1, argc - 1, argv + 1, local_specified, 0,
  357              repository);
  358     rev1_validated = 1;
  359     }
  360     if (rev2 != NULL && !rev2_validated)
  361     {
  362     tag_check_valid (rev2, argc - 1, argv + 1, local_specified, 0,
  363              repository);
  364     rev2_validated = 1;
  365     }
  366 
  367     /* start the recursion processor */
  368     err = start_recursion (patch_fileproc, (FILESDONEPROC)NULL, patch_dirproc,
  369                (DIRLEAVEPROC)NULL, NULL,
  370                argc - 1, argv + 1, local_specified,
  371                which, 0, CVS_LOCK_READ, where, 1, repository);
  372     free (repository);
  373     free (where);
  374 
  375     return err;
  376 }
  377 
  378 
  379 
  380 /*
  381  * Called to examine a particular RCS file, as appropriate with the options
  382  * that were set above.
  383  */
  384 /* ARGSUSED */
  385 static int
  386 patch_fileproc (callerdat, finfo)
  387     void *callerdat;
  388     struct file_info *finfo;
  389 {
  390     struct utimbuf t;
  391     char *vers_tag, *vers_head;
  392     char *rcs = NULL;
  393     char *rcs_orig = NULL;
  394     RCSNode *rcsfile;
  395     FILE *fp1, *fp2, *fp3;
  396     int ret = 0;
  397     int isattic = 0;
  398     int retcode = 0;
  399     char *file1;
  400     char *file2;
  401     char *strippath;
  402     char *line1, *line2;
  403     size_t line1_chars_allocated;
  404     size_t line2_chars_allocated;
  405     char *cp1, *cp2;
  406     FILE *fp;
  407     int line_length;
  408     int dargc = 0;
  409     size_t darg_allocated = 0;
  410     char **dargv = NULL;
  411 
  412     line1 = NULL;
  413     line1_chars_allocated = 0;
  414     line2 = NULL;
  415     line2_chars_allocated = 0;
  416     vers_tag = vers_head = NULL;
  417 
  418     /* find the parsed rcs file */
  419     if ((rcsfile = finfo->rcs) == NULL)
  420     {
  421     ret = 1;
  422     goto out2;
  423     }
  424     if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
  425     isattic = 1;
  426 
  427     rcs_orig = rcs = xmalloc (strlen (finfo->file) + sizeof (RCSEXT) + 5);
  428     (void) sprintf (rcs, "%s%s", finfo->file, RCSEXT);
  429 
  430     /* if vers_head is NULL, may have been removed from the release */
  431     if (isattic && rev2 == NULL && date2 == NULL)
  432     vers_head = NULL;
  433     else
  434     {
  435     vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match,
  436                     (int *) NULL);
  437     if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
  438     {
  439         free (vers_head);
  440         vers_head = NULL;
  441     }
  442     }
  443 
  444     if (toptwo_diffs)
  445     {
  446     if (vers_head == NULL)
  447     {
  448         ret = 1;
  449         goto out2;
  450     }
  451 
  452     if (!date1)
  453         date1 = xmalloc (MAXDATELEN);
  454     *date1 = '\0';
  455     if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == (time_t)-1)
  456     {
  457         if (!really_quiet)
  458         error (0, 0, "cannot find date in rcs file %s revision %s",
  459                rcs, vers_head);
  460         ret = 1;
  461         goto out2;
  462     }
  463     }
  464     vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match,
  465                    (int *) NULL);
  466     if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
  467     {
  468         free (vers_tag);
  469     vers_tag = NULL;
  470     }
  471 
  472     if ((vers_tag == NULL && vers_head == NULL) ||
  473         (vers_tag != NULL && vers_head != NULL &&
  474      strcmp (vers_head, vers_tag) == 0))
  475     {
  476     /* Nothing known about specified revs or
  477      * not changed between releases.
  478      */
  479     ret = 0;
  480     goto out2;
  481     }
  482 
  483     if( patch_short && ( vers_tag == NULL || vers_head == NULL ) )
  484     {
  485     /* For adds & removes with a short patch requested, we can print our
  486      * error message now and get out.
  487      */
  488     cvs_output ("File ", 0);
  489     cvs_output (finfo->fullname, 0);
  490     if (vers_tag == NULL)
  491     {
  492         cvs_output( " is new; ", 0 );
  493         cvs_output( rev2 ? rev2 : date2 ? date2 : "current", 0 );
  494         cvs_output( " revision ", 0 );
  495         cvs_output (vers_head, 0);
  496         cvs_output ("\n", 1);
  497     }
  498     else
  499     {
  500         cvs_output( " is removed; ", 0 );
  501         cvs_output( rev1 ? rev1 : date1, 0 );
  502         cvs_output( " revision ", 0 );
  503         cvs_output( vers_tag, 0 );
  504         cvs_output ("\n", 1);
  505     }
  506     ret = 0;
  507     goto out2;
  508     }
  509 
  510     /* Create 3 empty files.  I'm not really sure there is any advantage
  511      * to doing so now rather than just waiting until later.
  512      *
  513      * There is - cvs_temp_file opens the file so that it can guarantee that
  514      * we have exclusive write access to the file.  Unfortunately we spoil that
  515      * by closing it and reopening it again.  Of course any better solution
  516      * requires that the RCS functions accept open file pointers rather than
  517      * simple file names.
  518      */
  519     if ((fp1 = cvs_temp_file (&tmpfile1)) == NULL)
  520     {
  521     error (0, errno, "cannot create temporary file %s",
  522            tmpfile1 ? tmpfile1 : "(null)");
  523     ret = 1;
  524     goto out;
  525     }
  526     else
  527     if (fclose (fp1) < 0)
  528         error (0, errno, "warning: cannot close %s", tmpfile1);
  529     if ((fp2 = cvs_temp_file (&tmpfile2)) == NULL)
  530     {
  531     error (0, errno, "cannot create temporary file %s",
  532            tmpfile2 ? tmpfile2 : "(null)");
  533     ret = 1;
  534     goto out;
  535     }
  536     else
  537     if (fclose (fp2) < 0)
  538         error (0, errno, "warning: cannot close %s", tmpfile2);
  539     if ((fp3 = cvs_temp_file (&tmpfile3)) == NULL)
  540     {
  541     error (0, errno, "cannot create temporary file %s",
  542            tmpfile3 ? tmpfile3 : "(null)");
  543     ret = 1;
  544     goto out;
  545     }
  546     else
  547     if (fclose (fp3) < 0)
  548         error (0, errno, "warning: cannot close %s", tmpfile3);
  549 
  550     if (vers_tag != NULL)
  551     {
  552     retcode = RCS_checkout (rcsfile, (char *)NULL, vers_tag,
  553                 rev1, options, tmpfile1,
  554                 (RCSCHECKOUTPROC)NULL, (void *)NULL);
  555     if (retcode != 0)
  556     {
  557         error (0, 0,
  558            "cannot check out revision %s of %s", vers_tag, rcs);
  559         ret = 1;
  560         goto out;
  561     }
  562     memset ((char *) &t, 0, sizeof (t));
  563     if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
  564                             (char *) 0, 0)) != -1)
  565         /* I believe this timestamp only affects the dates in our diffs,
  566            and therefore should be on the server, not the client.  */
  567         (void) CVS_UTIME (tmpfile1, &t);
  568     }
  569     else if (toptwo_diffs)
  570     {
  571     ret = 1;
  572     goto out;
  573     }
  574     if (vers_head != NULL)
  575     {
  576     retcode = RCS_checkout (rcsfile, (char *)NULL, vers_head,
  577                 rev2, options, tmpfile2,
  578                 (RCSCHECKOUTPROC)NULL, (void *)NULL);
  579     if (retcode != 0)
  580     {
  581         error (0, 0,
  582            "cannot check out revision %s of %s", vers_head, rcs);
  583         ret = 1;
  584         goto out;
  585     }
  586     if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
  587                             (char *)0, 0)) != -1)
  588         /* I believe this timestamp only affects the dates in our diffs,
  589            and therefore should be on the server, not the client.  */
  590         (void)CVS_UTIME (tmpfile2, &t);
  591     }
  592 
  593     if (unidiff) run_add_arg_p (&dargc, &darg_allocated, &dargv, "-u");
  594     else run_add_arg_p (&dargc, &darg_allocated, &dargv, "-c");
  595     switch (diff_exec (tmpfile1, tmpfile2, NULL, NULL, dargc, dargv,
  596                tmpfile3))
  597     {
  598     case -1:            /* fork/wait failure */
  599         error (1, errno, "fork for diff failed on %s", rcs);
  600         break;
  601     case 0:             /* nothing to do */
  602         break;
  603     case 1:
  604         /*
  605          * The two revisions are really different, so read the first two
  606          * lines of the diff output file, and munge them to include more
  607          * reasonable file names that "patch" will understand, unless the
  608          * user wanted a short patch.  In that case, just output the short
  609          * message.
  610          */
  611         if( patch_short )
  612         {
  613         cvs_output ("File ", 0);
  614         cvs_output (finfo->fullname, 0);
  615         cvs_output (" changed from revision ", 0);
  616         cvs_output (vers_tag, 0);
  617         cvs_output (" to ", 0);
  618         cvs_output (vers_head, 0);
  619         cvs_output ("\n", 1);
  620         ret = 0;
  621         goto out;
  622         }
  623 
  624         /* Output an "Index:" line for patch to use */
  625         cvs_output ("Index: ", 0);
  626         cvs_output (finfo->fullname, 0);
  627         cvs_output ("\n", 1);
  628 
  629         /* Now the munging. */
  630         fp = open_file (tmpfile3, "r");
  631         if (getline (&line1, &line1_chars_allocated, fp) < 0 ||
  632         getline (&line2, &line2_chars_allocated, fp) < 0)
  633         {
  634         if (feof (fp))
  635             error (0, 0, "\
  636 failed to read diff file header %s for %s: end of file", tmpfile3, rcs);
  637         else
  638             error (0, errno,
  639                "failed to read diff file header %s for %s",
  640                tmpfile3, rcs);
  641         ret = 1;
  642         if (fclose (fp) < 0)
  643             error (0, errno, "error closing %s", tmpfile3);
  644         goto out;
  645         }
  646         if (!unidiff)
  647         {
  648         if (strncmp (line1, "*** ", 4) != 0 ||
  649             strncmp (line2, "--- ", 4) != 0 ||
  650             (cp1 = strchr (line1, '\t')) == NULL ||
  651             (cp2 = strchr (line2, '\t')) == NULL)
  652         {
  653             error (0, 0, "invalid diff header for %s", rcs);
  654             ret = 1;
  655             if (fclose (fp) < 0)
  656             error (0, errno, "error closing %s", tmpfile3);
  657             goto out;
  658         }
  659         }
  660         else
  661         {
  662         if (strncmp (line1, "--- ", 4) != 0 ||
  663             strncmp (line2, "+++ ", 4) != 0 ||
  664             (cp1 = strchr (line1, '\t')) == NULL ||
  665             (cp2 = strchr  (line2, '\t')) == NULL)
  666         {
  667             error (0, 0, "invalid unidiff header for %s", rcs);
  668             ret = 1;
  669             if (fclose (fp) < 0)
  670             error (0, errno, "error closing %s", tmpfile3);
  671             goto out;
  672         }
  673         }
  674         assert (current_parsed_root != NULL);
  675         assert (current_parsed_root->directory != NULL);
  676         {
  677         strippath = xmalloc (strlen (current_parsed_root->directory)
  678                                      + 2);
  679         (void)sprintf (strippath, "%s/",
  680                                current_parsed_root->directory);
  681         }
  682         /*else
  683         strippath = xstrdup (REPOS_STRIP); */
  684         if (strncmp (rcs, strippath, strlen (strippath)) == 0)
  685         rcs += strlen (strippath);
  686         free (strippath);
  687         if (vers_tag != NULL)
  688         {
  689         file1 = xmalloc (strlen (finfo->fullname)
  690                  + strlen (vers_tag)
  691                  + 10);
  692         (void)sprintf (file1, "%s:%s", finfo->fullname, vers_tag);
  693         }
  694         else
  695         {
  696         file1 = xstrdup (DEVNULL);
  697         }
  698         file2 = xmalloc (strlen (finfo->fullname)
  699                  + (vers_head != NULL ? strlen (vers_head) : 10)
  700                  + 10);
  701         (void)sprintf (file2, "%s:%s", finfo->fullname,
  702                vers_head ? vers_head : "removed");
  703 
  704         /* Note that the string "diff" is specified by POSIX (for -c)
  705            and is part of the diff output format, not the name of a
  706            program.  */
  707         if (unidiff)
  708         {
  709         cvs_output ("diff -u ", 0);
  710         cvs_output (file1, 0);
  711         cvs_output (" ", 1);
  712         cvs_output (file2, 0);
  713         cvs_output ("\n", 1);
  714 
  715         cvs_output ("--- ", 0);
  716         cvs_output (file1, 0);
  717         cvs_output (cp1, 0);
  718         cvs_output ("+++ ", 0);
  719         }
  720         else
  721         {
  722         cvs_output ("diff -c ", 0);
  723         cvs_output (file1, 0);
  724         cvs_output (" ", 1);
  725         cvs_output (file2, 0);
  726         cvs_output ("\n", 1);
  727 
  728         cvs_output ("*** ", 0);
  729         cvs_output (file1, 0);
  730         cvs_output (cp1, 0);
  731         cvs_output ("--- ", 0);
  732         }
  733 
  734         cvs_output (finfo->fullname, 0);
  735         cvs_output (cp2, 0);
  736 
  737         /* spew the rest of the diff out */
  738         while ((line_length
  739             = getline (&line1, &line1_chars_allocated, fp))
  740            >= 0)
  741         cvs_output (line1, 0);
  742         if (line_length < 0 && !feof (fp))
  743         error (0, errno, "cannot read %s", tmpfile3);
  744 
  745         if (fclose (fp) < 0)
  746         error (0, errno, "cannot close %s", tmpfile3);
  747         free (file1);
  748         free (file2);
  749         break;
  750     default:
  751         error (0, 0, "diff failed for %s", finfo->fullname);
  752     }
  753   out:
  754     if (line1)
  755         free (line1);
  756     if (line2)
  757         free (line2);
  758     if (tmpfile1 != NULL)
  759     {
  760     if (CVS_UNLINK (tmpfile1) < 0)
  761         error (0, errno, "cannot unlink %s", tmpfile1);
  762     free (tmpfile1);
  763     tmpfile1 = NULL;
  764     }
  765     if (tmpfile2 != NULL)
  766     {
  767     if (CVS_UNLINK (tmpfile2) < 0)
  768         error (0, errno, "cannot unlink %s", tmpfile2);
  769     free (tmpfile2);
  770     tmpfile2 = NULL;
  771     }
  772     if (tmpfile3 != NULL)
  773     {
  774     if (CVS_UNLINK (tmpfile3) < 0)
  775         error (0, errno, "cannot unlink %s", tmpfile3);
  776     free (tmpfile3);
  777     tmpfile3 = NULL;
  778     }
  779 
  780     if (dargc)
  781     {
  782     run_arg_free_p (dargc, dargv);
  783     free (dargv);
  784     }
  785 
  786  out2:
  787     if (vers_tag != NULL)
  788     free (vers_tag);
  789     if (vers_head != NULL)
  790     free (vers_head);
  791     if (rcs_orig)
  792     free (rcs_orig);
  793     return ret;
  794 }
  795 
  796 
  797 
  798 /*
  799  * Print a warm fuzzy message
  800  */
  801 /* ARGSUSED */
  802 static Dtype
  803 patch_dirproc (callerdat, dir, repos, update_dir, entries)
  804     void *callerdat;
  805     const char *dir;
  806     const char *repos;
  807     const char *update_dir;
  808     List *entries;
  809 {
  810     if (!quiet)
  811     error (0, 0, "Diffing %s", update_dir);
  812     return (R_PROCESS);
  813 }
  814 
  815 /*
  816  * Clean up temporary files
  817  */
  818 static RETSIGTYPE
  819 patch_cleanup ()
  820 {
  821     /* Note that the checks for existence_error are because we are
  822        called from a signal handler, without SIG_begincrsect, so
  823        we don't know whether the files got created.  */
  824 
  825     if (tmpfile1 != NULL)
  826     {
  827     if (unlink_file (tmpfile1) < 0
  828         && !existence_error (errno))
  829         error (0, errno, "cannot remove %s", tmpfile1);
  830     free (tmpfile1);
  831     }
  832     if (tmpfile2 != NULL)
  833     {
  834     if (unlink_file (tmpfile2) < 0
  835         && !existence_error (errno))
  836         error (0, errno, "cannot remove %s", tmpfile2);
  837     free (tmpfile2);
  838     }
  839     if (tmpfile3 != NULL)
  840     {
  841     if (unlink_file (tmpfile3) < 0
  842         && !existence_error (errno))
  843         error (0, errno, "cannot remove %s", tmpfile3);
  844     free (tmpfile3);
  845     }
  846     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
  847 }