home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / cvs-1.8.7-src.tgz / tar.out / fsf / cvs / src / patch.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  16KB  |  645 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.4 kit.
  7.  * 
  8.  * Patch
  9.  * 
  10.  * Create a Larry Wall format "patch" file between a previous release and the
  11.  * current head of a module, or between two releases.  Can specify the
  12.  * release as either a date or a revision number.
  13.  */
  14.  
  15. #include "cvs.h"
  16. #include "getline.h"
  17.  
  18. static RETSIGTYPE patch_cleanup PROTO((void));
  19. static Dtype patch_dirproc PROTO ((void *callerdat, char *dir,
  20.                    char *repos, char *update_dir,
  21.                    List *entries));
  22. static int patch_fileproc PROTO ((void *callerdat, struct file_info *finfo));
  23. static int patch_proc PROTO((int *pargc, char **argv, char *xwhere,
  24.                char *mwhere, char *mfile, int shorten,
  25.                int local_specified, char *mname, char *msg));
  26.  
  27. static int force_tag_match = 1;
  28. static int patch_short = 0;
  29. static int toptwo_diffs = 0;
  30. static int local = 0;
  31. static char *options = NULL;
  32. static char *rev1 = NULL;
  33. static int rev1_validated = 0;
  34. static char *rev2 = NULL;
  35. static int rev2_validated = 0;
  36. static char *date1 = NULL;
  37. static char *date2 = NULL;
  38. static char *tmpfile1 = NULL;
  39. static char *tmpfile2 = NULL;
  40. static char *tmpfile3 = NULL;
  41. static int unidiff = 0;
  42.  
  43. static const char *const patch_usage[] =
  44. {
  45.     "Usage: %s %s [-fl] [-c|-u] [-s|-t] [-V %%d]\n",
  46.     "    -r rev|-D date [-r rev2 | -D date2] modules...\n",
  47.     "\t-f\tForce a head revision match if tag/date not found.\n",
  48.     "\t-l\tLocal directory only, not recursive\n",
  49.     "\t-c\tContext diffs (default)\n",
  50.     "\t-u\tUnidiff format.\n",
  51.     "\t-s\tShort patch - one liner per file.\n",
  52.     "\t-t\tTop two diffs - last change made to the file.\n",
  53.     "\t-D date\tDate.\n",
  54.     "\t-r rev\tRevision - symbolic or numeric.\n",
  55.     "\t-V vers\tUse RCS Version \"vers\" for keyword expansion.\n",
  56.     NULL
  57. };
  58.  
  59. int
  60. patch (argc, argv)
  61.     int argc;
  62.     char **argv;
  63. {
  64.     register int i;
  65.     int c;
  66.     int err = 0;
  67.     DBM *db;
  68.  
  69.     if (argc == -1)
  70.     usage (patch_usage);
  71.  
  72.     optind = 1;
  73.     while ((c = getopt (argc, argv, "V:k:cuftsQqlRD:r:")) != -1)
  74.     {
  75.     switch (c)
  76.     {
  77.         case 'Q':
  78.         case 'q':
  79. #ifdef SERVER_SUPPORT
  80.         /* The CVS 1.5 client sends these options (in addition to
  81.            Global_option requests), so we must ignore them.  */
  82.         if (!server_active)
  83. #endif
  84.             error (1, 0,
  85.                "-q or -Q must be specified before \"%s\"",
  86.                command_name);
  87.         break;
  88.         case 'f':
  89.         force_tag_match = 0;
  90.         break;
  91.         case 'l':
  92.         local = 1;
  93.         break;
  94.         case 'R':
  95.         local = 0;
  96.         break;
  97.         case 't':
  98.         toptwo_diffs = 1;
  99.         break;
  100.         case 's':
  101.         patch_short = 1;
  102.         break;
  103.         case 'D':
  104.         if (rev2 != NULL || date2 != NULL)
  105.             error (1, 0,
  106.                "no more than two revisions/dates can be specified");
  107.         if (rev1 != NULL || date1 != NULL)
  108.             date2 = Make_Date (optarg);
  109.         else
  110.             date1 = Make_Date (optarg);
  111.         break;
  112.         case 'r':
  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.             rev2 = optarg;
  118.         else
  119.             rev1 = optarg;
  120.         break;
  121.         case 'k':
  122.         if (options)
  123.             free (options);
  124.         options = RCS_check_kflag (optarg);
  125.         break;
  126.         case 'V':
  127.         if (atoi (optarg) <= 0)
  128.             error (1, 0, "must specify a version number to -V");
  129.         if (options)
  130.             free (options);
  131.         options = xmalloc (strlen (optarg) + 1 + 2);    /* for the -V */
  132.         (void) sprintf (options, "-V%s", optarg);
  133.         break;
  134.         case 'u':
  135.         unidiff = 1;        /* Unidiff */
  136.         break;
  137.         case 'c':            /* Context diff */
  138.         unidiff = 0;
  139.         break;
  140.         case '?':
  141.         default:
  142.         usage (patch_usage);
  143.         break;
  144.     }
  145.     }
  146.     argc -= optind;
  147.     argv += optind;
  148.  
  149.     /* Sanity checks */
  150.     if (argc < 1)
  151.     usage (patch_usage);
  152.  
  153.     if (toptwo_diffs && patch_short)
  154.     error (1, 0, "-t and -s options are mutually exclusive");
  155.     if (toptwo_diffs && (date1 != NULL || date2 != NULL ||
  156.              rev1 != NULL || rev2 != NULL))
  157.     error (1, 0, "must not specify revisions/dates with -t option!");
  158.  
  159.     if (!toptwo_diffs && (date1 == NULL && date2 == NULL &&
  160.               rev1 == NULL && rev2 == NULL))
  161.     error (1, 0, "must specify at least one revision/date!");
  162.     if (date1 != NULL && date2 != NULL)
  163.     if (RCS_datecmp (date1, date2) >= 0)
  164.         error (1, 0, "second date must come after first date!");
  165.  
  166.     /* if options is NULL, make it a NULL string */
  167.     if (options == NULL)
  168.     options = xstrdup ("");
  169.  
  170. #ifdef CLIENT_SUPPORT
  171.     if (client_active)
  172.     {
  173.     /* We're the client side.  Fire up the remote server.  */
  174.     start_server ();
  175.     
  176.     ign_setup ();
  177.  
  178.     if (local)
  179.         send_arg("-l");
  180.     if (force_tag_match)
  181.         send_arg("-f");
  182.     if (toptwo_diffs)
  183.         send_arg("-t");
  184.     if (patch_short)
  185.         send_arg("-s");
  186.     if (unidiff)
  187.         send_arg("-u");
  188.  
  189.     if (rev1)
  190.         option_with_arg ("-r", rev1);
  191.     if (date1)
  192.         client_senddate (date1);
  193.     if (rev2)
  194.         option_with_arg ("-r", rev2);
  195.     if (date2)
  196.         client_senddate (date2);
  197.     if (options[0] != '\0')
  198.         send_arg (options);
  199.  
  200.     {
  201.         int i;
  202.         for (i = 0; i < argc; ++i)
  203.         send_arg (argv[i]);
  204.     }
  205.  
  206.     send_to_server ("rdiff\012", 0);
  207.         return get_responses_and_close ();
  208.     }
  209. #endif
  210.  
  211.     /* clean up if we get a signal */
  212. #ifdef SIGHUP
  213.     (void) SIG_register (SIGHUP, patch_cleanup);
  214. #endif
  215. #ifdef SIGINT
  216.     (void) SIG_register (SIGINT, patch_cleanup);
  217. #endif
  218. #ifdef SIGQUIT
  219.     (void) SIG_register (SIGQUIT, patch_cleanup);
  220. #endif
  221. #ifdef SIGPIPE
  222.     (void) SIG_register (SIGPIPE, patch_cleanup);
  223. #endif
  224. #ifdef SIGTERM
  225.     (void) SIG_register (SIGTERM, patch_cleanup);
  226. #endif
  227.  
  228.     db = open_module ();
  229.     for (i = 0; i < argc; i++)
  230.     err += do_module (db, argv[i], PATCH, "Patching", patch_proc,
  231.               (char *) NULL, 0, 0, 0, (char *) NULL);
  232.     close_module (db);
  233.     free (options);
  234.     patch_cleanup ();
  235.     return (err);
  236. }
  237.  
  238. /*
  239.  * callback proc for doing the real work of patching
  240.  */
  241. /* ARGSUSED */
  242. static char where[PATH_MAX];
  243. static int
  244. patch_proc (pargc, argv, xwhere, mwhere, mfile, shorten, local_specified,
  245.         mname, msg)
  246.     int *pargc;
  247.     char **argv;
  248.     char *xwhere;
  249.     char *mwhere;
  250.     char *mfile;
  251.     int shorten;
  252.     int local_specified;
  253.     char *mname;
  254.     char *msg;
  255. {
  256.     int err = 0;
  257.     int which;
  258.     char repository[PATH_MAX];
  259.  
  260.     (void) sprintf (repository, "%s/%s", CVSroot_directory, argv[0]);
  261.     (void) strcpy (where, argv[0]);
  262.  
  263.     /* if mfile isn't null, we need to set up to do only part of the module */
  264.     if (mfile != NULL)
  265.     {
  266.     char *cp;
  267.     char path[PATH_MAX];
  268.  
  269.     /* if the portion of the module is a path, put the dir part on repos */
  270.     if ((cp = strrchr (mfile, '/')) != NULL)
  271.     {
  272.         *cp = '\0';
  273.         (void) strcat (repository, "/");
  274.         (void) strcat (repository, mfile);
  275.         (void) strcat (where, "/");
  276.         (void) strcat (where, mfile);
  277.         mfile = cp + 1;
  278.     }
  279.  
  280.     /* take care of the rest */
  281.     (void) sprintf (path, "%s/%s", repository, mfile);
  282.     if (isdir (path))
  283.     {
  284.         /* directory means repository gets the dir tacked on */
  285.         (void) strcpy (repository, path);
  286.         (void) strcat (where, "/");
  287.         (void) strcat (where, mfile);
  288.     }
  289.     else
  290.     {
  291.         int i;
  292.  
  293.         /* a file means muck argv */
  294.         for (i = 1; i < *pargc; i++)
  295.         free (argv[i]);
  296.         argv[1] = xstrdup (mfile);
  297.         (*pargc) = 2;
  298.     }
  299.     }
  300.  
  301.     /* cd to the starting repository */
  302.     if ( CVS_CHDIR (repository) < 0)
  303.     {
  304.     error (0, errno, "cannot chdir to %s", repository);
  305.     return (1);
  306.     }
  307.  
  308.     if (force_tag_match)
  309.     which = W_REPOS | W_ATTIC;
  310.     else
  311.     which = W_REPOS;
  312.  
  313.     if (rev1 != NULL && !rev1_validated)
  314.     {
  315.     tag_check_valid (rev1, *pargc - 1, argv + 1, local, 0, NULL);
  316.     rev1_validated = 1;
  317.     }
  318.     if (rev2 != NULL && !rev2_validated)
  319.     {
  320.     tag_check_valid (rev2, *pargc - 1, argv + 1, local, 0, NULL);
  321.     rev2_validated = 1;
  322.     }
  323.  
  324.     /* start the recursion processor */
  325.     err = start_recursion (patch_fileproc, (FILESDONEPROC) NULL, patch_dirproc,
  326.                (DIRLEAVEPROC) NULL, NULL,
  327.                *pargc - 1, argv + 1, local,
  328.                which, 0, 1, where, 1);
  329.  
  330.     return (err);
  331. }
  332.  
  333. /*
  334.  * Called to examine a particular RCS file, as appropriate with the options
  335.  * that were set above.
  336.  */
  337. /* ARGSUSED */
  338. static int
  339. patch_fileproc (callerdat, finfo)
  340.     void *callerdat;
  341.     struct file_info *finfo;
  342. {
  343.     struct utimbuf t;
  344.     char *vers_tag, *vers_head;
  345.     char rcsspace[1][PATH_MAX];
  346.     char *rcs = rcsspace[0];
  347.     RCSNode *rcsfile;
  348.     FILE *fp1, *fp2, *fp3;
  349.     int ret = 0;
  350.     int isattic = 0;
  351.     int retcode = 0;
  352.     char file1[PATH_MAX], file2[PATH_MAX], strippath[PATH_MAX];
  353.     char *line1, *line2;
  354.     size_t line1_chars_allocated;
  355.     size_t line2_chars_allocated;
  356.     char *cp1, *cp2;
  357.     FILE *fp;
  358.  
  359.     /* find the parsed rcs file */
  360.     if ((rcsfile = finfo->rcs) == NULL)
  361.     return (1);
  362.     if ((rcsfile->flags & VALID) && (rcsfile->flags & INATTIC))
  363.     isattic = 1;
  364.  
  365.     (void) sprintf (rcs, "%s%s", finfo->file, RCSEXT);
  366.  
  367.     /* if vers_head is NULL, may have been removed from the release */
  368.     if (isattic && rev2 == NULL && date2 == NULL)
  369.     vers_head = NULL;
  370.     else
  371.     {
  372.     vers_head = RCS_getversion (rcsfile, rev2, date2, force_tag_match, 0);
  373.     if (vers_head != NULL && RCS_isdead (rcsfile, vers_head))
  374.     {
  375.         free (vers_head);
  376.         vers_head = NULL;
  377.     }
  378.     }
  379.  
  380.     if (toptwo_diffs)
  381.     {
  382.     if (vers_head == NULL)
  383.         return (1);
  384.  
  385.     if (!date1)
  386.         date1 = xmalloc (50);    /* plenty big :-) */
  387.     *date1 = '\0';
  388.     if (RCS_getrevtime (rcsfile, vers_head, date1, 1) == -1)
  389.     {
  390.         if (!really_quiet)
  391.         error (0, 0, "cannot find date in rcs file %s revision %s",
  392.                rcs, vers_head);
  393.         return (1);
  394.     }
  395.     }
  396.     vers_tag = RCS_getversion (rcsfile, rev1, date1, force_tag_match, 0);
  397.     if (vers_tag != NULL && RCS_isdead (rcsfile, vers_tag))
  398.     {
  399.         free (vers_tag);
  400.     vers_tag = NULL;
  401.     }
  402.  
  403.     if (vers_tag == NULL && vers_head == NULL)
  404.     return (0);            /* nothing known about specified revs */
  405.  
  406.     if (vers_tag && vers_head && strcmp (vers_head, vers_tag) == 0)
  407.     return (0);            /* not changed between releases */
  408.  
  409.     if (patch_short)
  410.     {
  411.     (void) printf ("File %s ", finfo->fullname);
  412.     if (vers_tag == NULL)
  413.         (void) printf ("is new; current revision %s\n", vers_head);
  414.     else if (vers_head == NULL)
  415.     {
  416.         (void) printf ("is removed; not included in ");
  417.         if (rev2 != NULL)
  418.         (void) printf ("release tag %s", rev2);
  419.         else if (date2 != NULL)
  420.         (void) printf ("release date %s", date2);
  421.         else
  422.         (void) printf ("current release");
  423.         (void) printf ("\n");
  424.     }
  425.     else
  426.         (void) printf ("changed from revision %s to %s\n",
  427.                vers_tag, vers_head);
  428.     return (0);
  429.     }
  430.     tmpfile1 = cvs_temp_name ();
  431.     if ((fp1 = CVS_FOPEN (tmpfile1, "w+")) != NULL)
  432.     (void) fclose (fp1);
  433.     tmpfile2 = cvs_temp_name ();
  434.     if ((fp2 = CVS_FOPEN (tmpfile2, "w+")) != NULL)
  435.     (void) fclose (fp2);
  436.     tmpfile3 = cvs_temp_name ();
  437.     if ((fp3 = CVS_FOPEN (tmpfile3, "w+")) != NULL)
  438.     (void) fclose (fp3);
  439.     if (fp1 == NULL || fp2 == NULL || fp3 == NULL)
  440.     {
  441.     /* FIXME: should be printing a proper error message, with errno-based
  442.        message, and the filename which we could not create.  */
  443.     error (0, 0, "cannot create temporary files");
  444.     ret = 1;
  445.     goto out;
  446.     }
  447.     if (vers_tag != NULL)
  448.     {
  449.     retcode = RCS_fast_checkout (rcsfile, NULL, vers_tag, options,
  450.                      tmpfile1, 0);
  451.     if (retcode != 0)
  452.     {
  453.         if (!really_quiet)
  454.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  455.                "co of revision %s in %s failed", vers_tag, rcs);
  456.         ret = 1;
  457.         goto out;
  458.     }
  459.     memset ((char *) &t, 0, sizeof (t));
  460.     if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag,
  461.                             (char *) 0, 0)) != -1)
  462.         (void) utime (tmpfile1, &t);
  463.     }
  464.     else if (toptwo_diffs)
  465.     {
  466.     ret = 1;
  467.     goto out;
  468.     }
  469.     if (vers_head != NULL)
  470.     {
  471.     retcode = RCS_fast_checkout (rcsfile, NULL, vers_head, options,
  472.                      tmpfile2, 0);
  473.     if (retcode != 0)
  474.     {
  475.         if (!really_quiet)
  476.         error (retcode == -1 ? 1 : 0, retcode == -1 ? errno : 0,
  477.                "co of revision %s in %s failed", vers_head, rcs);
  478.         ret = 1;
  479.         goto out;
  480.     }
  481.     if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head,
  482.                             (char *) 0, 0)) != -1)
  483.         (void) utime (tmpfile2, &t);
  484.     }
  485.     run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c');
  486.     run_arg (tmpfile1);
  487.     run_arg (tmpfile2);
  488.  
  489.     line1 = NULL;
  490.     line1_chars_allocated = 0;
  491.     line2 = NULL;
  492.     line2_chars_allocated = 0;
  493.  
  494.     switch (run_exec (RUN_TTY, tmpfile3, RUN_TTY, RUN_REALLY))
  495.     {
  496.     case -1:            /* fork/wait failure */
  497.         error (1, errno, "fork for diff failed on %s", rcs);
  498.         break;
  499.     case 0:                /* nothing to do */
  500.         break;
  501.     case 1:
  502.         /*
  503.          * The two revisions are really different, so read the first two
  504.          * lines of the diff output file, and munge them to include more
  505.          * reasonable file names that "patch" will understand.
  506.          */
  507.  
  508.         /* Output an "Index:" line for patch to use */
  509.         (void) fflush (stdout);
  510.         (void) printf ("Index: %s\n", finfo->fullname);
  511.         (void) fflush (stdout);
  512.  
  513.         fp = open_file (tmpfile3, "r");
  514.         if (getline (&line1, &line1_chars_allocated, fp) < 0 ||
  515.         getline (&line2, &line2_chars_allocated, fp) < 0)
  516.         {
  517.         error (0, errno, "failed to read diff file header %s for %s",
  518.                tmpfile3, rcs);
  519.         ret = 1;
  520.         (void) fclose (fp);
  521.         goto out;
  522.         }
  523.         if (!unidiff)
  524.         {
  525.         if (strncmp (line1, "*** ", 4) != 0 ||
  526.             strncmp (line2, "--- ", 4) != 0 ||
  527.             (cp1 = strchr (line1, '\t')) == NULL ||
  528.             (cp2 = strchr (line2, '\t')) == NULL)
  529.         {
  530.             error (0, 0, "invalid diff header for %s", rcs);
  531.             ret = 1;
  532.             (void) fclose (fp);
  533.             goto out;
  534.         }
  535.         }
  536.         else
  537.         {
  538.         if (strncmp (line1, "--- ", 4) != 0 ||
  539.             strncmp (line2, "+++ ", 4) != 0 ||
  540.             (cp1 = strchr (line1, '\t')) == NULL ||
  541.             (cp2 = strchr  (line2, '\t')) == NULL)
  542.         {
  543.             error (0, 0, "invalid unidiff header for %s", rcs);
  544.             ret = 1;
  545.             (void) fclose (fp);
  546.             goto out;
  547.         }
  548.         }
  549.         if (CVSroot_directory != NULL)
  550.         (void) sprintf (strippath, "%s/", CVSroot_directory);
  551.         else
  552.         (void) strcpy (strippath, REPOS_STRIP);
  553.         if (strncmp (rcs, strippath, strlen (strippath)) == 0)
  554.         rcs += strlen (strippath);
  555.         if (vers_tag != NULL)
  556.         {
  557.         (void) sprintf (file1, "%s:%s", finfo->fullname, vers_tag);
  558.         }
  559.         else
  560.         {
  561.         (void) strcpy (file1, DEVNULL);
  562.         }
  563.         (void) sprintf (file2, "%s:%s", finfo->fullname,
  564.                 vers_head ? vers_head : "removed");
  565.  
  566.         /* Note that this prints "diff" not DIFF.  The format of a diff
  567.            does not depend on the name of the program which happens to
  568.            have produced it.  */
  569.         if (unidiff)
  570.         {
  571.         (void) printf ("diff -u %s %s\n", file1, file2);
  572.         (void) printf ("--- %s%s+++ ", file1, cp1);
  573.         }
  574.         else
  575.         {
  576.         (void) printf ("diff -c %s %s\n", file1, file2);
  577.         (void) printf ("*** %s%s--- ", file1, cp1);
  578.         }
  579.  
  580.         (void) printf ("%s%s", finfo->fullname, cp2);
  581.         /* spew the rest of the diff out */
  582.         while (getline (&line1, &line1_chars_allocated, fp) >= 0)
  583.         (void) fputs (line1, stdout);
  584.         (void) fclose (fp);
  585.         break;
  586.     default:
  587.         error (0, 0, "diff failed for %s", finfo->fullname);
  588.     }
  589.   out:
  590.     if (line1)
  591.         free (line1);
  592.     if (line2)
  593.         free (line2);
  594.     /* FIXME: should be checking for errors.  */
  595.     (void) CVS_UNLINK (tmpfile1);
  596.     (void) CVS_UNLINK (tmpfile2);
  597.     (void) CVS_UNLINK (tmpfile3);
  598.     free (tmpfile1);
  599.     free (tmpfile2);
  600.     free (tmpfile3);
  601.     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
  602.     return (ret);
  603. }
  604.  
  605. /*
  606.  * Print a warm fuzzy message
  607.  */
  608. /* ARGSUSED */
  609. static Dtype
  610. patch_dirproc (callerdat, dir, repos, update_dir, entries)
  611.     void *callerdat;
  612.     char *dir;
  613.     char *repos;
  614.     char *update_dir;
  615.     List *entries;
  616. {
  617.     if (!quiet)
  618.     error (0, 0, "Diffing %s", update_dir);
  619.     return (R_PROCESS);
  620. }
  621.  
  622. /*
  623.  * Clean up temporary files
  624.  */
  625. static RETSIGTYPE
  626. patch_cleanup ()
  627. {
  628.     if (tmpfile1 != NULL)
  629.     {
  630.     (void) unlink_file (tmpfile1);
  631.     free (tmpfile1);
  632.     }
  633.     if (tmpfile2 != NULL)
  634.     {
  635.     (void) unlink_file (tmpfile2);
  636.     free (tmpfile2);
  637.     }
  638.     if (tmpfile3 != NULL)
  639.     {
  640.     (void) unlink_file (tmpfile3);
  641.     free (tmpfile3);
  642.     }
  643.     tmpfile1 = tmpfile2 = tmpfile3 = NULL;
  644. }
  645.