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