home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / prgramer / rcs / sources / patch.c < prev    next >
C/C++ Source or Header  |  1992-02-23  |  11KB  |  399 lines

  1. #ifndef lint
  2. static char rcsid[] = "$Id: patch.c,v 1.6.1.1 91/01/18 12:18:45 berliner Exp $";
  3. #endif 
  4.  
  5. /*
  6.  *    Copyright (c) 1989, Brian Berliner
  7.  *
  8.  *    You may distribute under the terms of the GNU General Public License
  9.  *    as specified in the README file that comes with the CVS 1.0 kit.
  10.  *
  11.  * Patch
  12.  *
  13.  *    Create a Larry Wall format "patch" file between a previous release
  14.  *    and the current head of a module, or between two releases.  Can
  15.  *    specify the release as either a date or a revision number.
  16.  */
  17.  
  18. #include <time.h>
  19. #include <fcntl.h>
  20. #include <signal.h>
  21. #include <ctype.h>
  22. #include <sys/types.h>
  23. #include "dir.h"
  24. #include "cvs.h"
  25.  
  26. extern char update_dir[];
  27. extern DBM *open_module();
  28. extern int force_tag_match;
  29.  
  30. static int patch_recursive = 1;
  31. static int patch_short = 0;
  32. static int toptwo_diffs = 0;
  33. static char rev1[50], rev2[50], date1[50], date2[50];
  34. static char tmpfile1[MAXPATHLEN], tmpfile2[MAXPATHLEN], tmpfile3[MAXPATHLEN];
  35.  
  36. void patch_cleanup();
  37.  
  38. patch(argc, argv)
  39.     int argc;
  40.     char *argv[];
  41. {
  42.     register int i;
  43.     int c, err = 0;
  44.     DBM *db;
  45.  
  46.     if (argc == -1)
  47.     patch_usage();
  48.     rev1[0] = rev2[0] = date1[0] = date2[0] = '\0';
  49.     optind = 1;
  50.     while ((c = getopt(argc, argv, "ftsQqlD:r:")) != -1) {
  51.     switch (c) {
  52.     case 'Q':
  53.         really_quiet = 1;
  54.         /* FALL THROUGH */
  55.     case 'q':
  56.         quiet = 1;
  57.         break;
  58.     case 'f':
  59.         force_tag_match = 1;
  60.         break;
  61.     case 'l':
  62.         patch_recursive = 0;
  63.         break;
  64.     case 't':
  65.         toptwo_diffs = 1;
  66.         break;
  67.     case 's':
  68.         patch_short = 1;
  69.         break;
  70.     case 'D':
  71.         if (rev2[0] != '\0' || date2[0] != '\0')
  72.         error(0, "no more than two revisions/dates can be specified");
  73.         if (rev1[0] != '\0' || date1[0] != '\0')
  74.         Make_Date(optarg, date2);
  75.         else
  76.         Make_Date(optarg, date1);
  77.         break;
  78.     case 'r':
  79.         if (rev2[0] != '\0' || date2[0] != '\0')
  80.         error(0, "no more than two revisions/dates can be specified");
  81.         if (rev1[0] != '\0' || date1[0] != '\0')
  82.         (void) strcpy(rev2, optarg);
  83.         else
  84.         (void) strcpy(rev1, optarg);
  85.         break;
  86.     case '?':
  87.     default:
  88.         patch_usage();
  89.         break;
  90.     }
  91.     }
  92.     argc -= optind;
  93.     argv += optind;
  94.     if (argc < 1)
  95.     patch_usage();
  96.     if (toptwo_diffs && patch_short)
  97.     error(0, "-t and -s options are mutually exclusive");
  98.     if (toptwo_diffs && (date1[0] != '\0' || date2[0] != '\0' ||
  99.                  rev1[0] != '\0' || rev2[0] != '\0')) {
  100.     error(0, "must not specify revisions/dates with -t option!");
  101.     }
  102.     if (!toptwo_diffs &&
  103.     (date1[0] == '\0' && date2[0] == '\0' &&
  104.      rev1[0] == '\0' && rev2[0] == '\0')) {
  105.     error(0, "must specify at least one revision/date!");
  106.     }
  107.     if (date1[0] != '\0' && date2[0] != '\0') {
  108.     if (datecmp(date1, date2) >= 0)
  109.         error(0, "second date must come after first date!");
  110.     }
  111.     (void) signal(SIGINT, patch_cleanup);
  112.     (void) signal(SIGTERM, patch_cleanup);
  113.     (void) signal(SIGBREAK, patch_cleanup);
  114.     db = open_module();
  115.     for (i = 0; i < argc; i++)
  116.     err += do_module(db, argv[i], PATCH, "Examining");
  117.     close_module(db);
  118.     patch_cleanup(0);
  119.     exit(err);
  120. }
  121.  
  122. /*
  123.  * This is the recursive function that looks to see if an RCS file
  124.  * has been modified since the revision specified in rev1 or date1,
  125.  * using the revision specified in rev2 or date2 or the head if
  126.  * neither are specified.
  127.  *
  128.  * If the "rcs" argument is NULL, descend the current
  129.  * directory, examining all the files as appropriate; otherwise, just
  130.  * examine the argument rcs file
  131.  */
  132. patched(rcs)
  133.     char *rcs;
  134. {
  135.     DIR *dirp;
  136.     struct dirent *dp;
  137.     char line[10];
  138.     char *cp;
  139.     int err = 0;
  140.  
  141.     if (rcs == NULL) {
  142.     if ((dirp = opendir(".")) == NULL) {
  143.         err++;
  144.     } else {
  145.         (void) sprintf(line, ".*%s$", RCSEXT);
  146.         if ((cp = re_comp(line)) != NULL) {
  147.         warn(0, "%s", cp);
  148.         err++;
  149.         } else while ((dp = readdir(dirp)) != NULL) {
  150.         if (strcmp(dp->d_name, ".") == 0 ||
  151.             strcmp(dp->d_name, "..") == 0 ||
  152.             stricmp(dp->d_name, CVSATTIC) == 0 ||
  153.             stricmp(dp->d_name, CVSLCK) == 0)
  154.             continue;
  155.         if (isdir(dp->d_name) && patch_recursive) {
  156.             char cwd[MAXPATHLEN];
  157.  
  158.             if (getwd(cwd) == NULL) {
  159.             warn(0, "cannot get working directory: %s", cwd);
  160.             err++;
  161.             continue;
  162.             }
  163.             if (update_dir[0] == '\0') {
  164.             (void) strcpy(update_dir, dp->d_name);
  165.             } else {
  166.             (void) strcat(update_dir, DIRSEPSTR);
  167.             (void) strcat(update_dir, dp->d_name);
  168.             }
  169.             if (!quiet) {
  170.             printf("%s %s: Examining %s\n",
  171.                    progname, command, update_dir);
  172.             }
  173.             if (chdir(dp->d_name) < 0) {
  174.             warn(1, "chdir failed; %s ignored", update_dir);
  175.             err++;
  176.             continue;
  177.             }
  178.             err += patched((char *)0);
  179.             if ((cp = rindex_sep(update_dir)) != NULL)
  180.             *cp = '\0';
  181.             else
  182.             update_dir[0] = '\0';
  183.             if (chdir(cwd) < 0)
  184.             error(1, "cannot chdir back to %s", cwd);
  185.             continue;
  186.         }
  187.         if (re_exec(dp->d_name))
  188.             err += patch_file(dp->d_name);
  189.         }
  190.     }
  191.     if (dirp)
  192.         (void) closedir(dirp);
  193.     } else {
  194.     return (patch_file(rcs));
  195.     }
  196.     return (err);
  197. }
  198.  
  199. /*
  200.  * Called to examine a particular RCS file, as appropriate with the options
  201.  * that were set above.
  202.  */
  203. static
  204. patch_file(rcs)
  205.     char *rcs;
  206. {
  207.     char vers_tag[50], vers_head[50];
  208.     int fd1, fd2, fd3, ret = 0;
  209.  
  210.     Version_Number(rcs, rev2, date2, vers_head);
  211.     if (vers_head[0] == '\0') {
  212.     if (!really_quiet)
  213.         warn(0, "cannot find head revision for %s", rcs);
  214.     return (1);
  215.     }
  216.     if (toptwo_diffs && get_rcsdate(rcs, vers_head, date1) != 0) {
  217.     if (!really_quiet)
  218.         warn(0, "cannot find date in rcs file %s revision %s",
  219.          rcs, vers_head);
  220.     return (1);
  221.     }
  222.     Version_Number(rcs, rev1, date1, vers_tag);
  223.     if (strcmp(vers_head, vers_tag) == 0)
  224.     return (0);            /* not changed between releases */
  225.     if (patch_short) {
  226.     printf("File ");
  227.     if (update_dir[0] != '\0')
  228.         printf("%s%c", update_dir, DIRSEP);
  229.     if (vers_tag[0] == '\0')
  230.         printf("%s is new; current revision %s\n", rcs, vers_head);
  231.     else
  232.         printf("%s changed from revision %s to %s\n",
  233.            rcs, vers_tag, vers_head);
  234.     return (0);
  235.     }
  236.     (void) sprintf(tmpfile1, "%s%ccpXXXXXX", Tmpdir, DIRSEP);
  237.     (void) sprintf(tmpfile2, "%s%ccpXXXXXX", Tmpdir, DIRSEP);
  238.     (void) sprintf(tmpfile3, "%s%ccpXXXXXX", Tmpdir, DIRSEP);
  239.     fd1 = mkstemp(tmpfile1);    (void) close(fd1);
  240.     fd2 = mkstemp(tmpfile2);    (void) close(fd2);
  241.     fd3 = mkstemp(tmpfile3);    (void) close(fd3);
  242.     if (fd1 < 0 || fd2 < 0 || fd3 < 0) {
  243.     warn(0, "cannot create temporary files");
  244.     ret = 1;
  245.     goto out;
  246.     }
  247.     if (vers_tag[0] != '\0') {
  248.     (void) sprintf(prog, "%s -p -q -r%s %s >%s", RCS_CO,
  249.                vers_tag, rcs, tmpfile1);
  250.     if (system(prog) != 0) {
  251.         if (!really_quiet)
  252.         warn(0, "co of revision %s for %s failed", VN_Rcs, rcs);
  253.         ret = 1;
  254.         goto out;
  255.     }
  256.     } else if (toptwo_diffs) {
  257.     ret = 1;
  258.     goto out;
  259.     }
  260.     (void) sprintf(prog, "%s -p -q -r%s %s >%s", RCS_CO,
  261.            vers_head, rcs, tmpfile2);
  262.     if (system(prog) != 0) {
  263.     if (!really_quiet)
  264.         warn(0, "co of revision %s for %s failed", VN_Rcs, rcs);
  265.     return (1);
  266.     }
  267.     (void) sprintf(prog, "%s -c %s %s >%s", DIFF, tmpfile1,
  268.            tmpfile2, tmpfile3);
  269.     if (system(prog) != 0) {
  270.     char file1[MAXPATHLEN], file2[MAXPATHLEN], strippath[MAXPATHLEN];
  271.     char line1[MAXLINELEN], line2[MAXLINELEN];
  272.     char *cp1, *cp2;
  273.     FILE *fp;
  274.  
  275.     /*
  276.      * The two revisions are really different, so read the first
  277.      * two lines of the diff output file, and munge them to include
  278.      * more reasonable file names that "patch" will understand.
  279.      */
  280.     fp = open_file(tmpfile3, "r");
  281.     if (fgets(line1, sizeof(line1), fp) == NULL ||
  282.         fgets(line2, sizeof(line2), fp) == NULL) {
  283.         warn(1, "failed to read diff file header %s for %s",
  284.          tmpfile3, rcs);
  285.         ret = 1;
  286.         (void) fclose(fp);
  287.         goto out;
  288.     }
  289.     if (strncmp(line1,"*** ",4) != 0 || strncmp(line2,"--- ",4) != 0 ||
  290.         (cp1 = index(line1, '\t')) == NULL ||
  291.         (cp2 = index(line2, '\t')) == NULL) {
  292.         warn(0, "invalid diff header for %s", rcs);
  293.         ret = 1;
  294.         (void) fclose(fp);
  295.         goto out;
  296.     }
  297.     if (CVSroot != NULL)
  298.         (void) sprintf(strippath, "%s%c", CVSroot, DIRSEP);
  299.     else
  300.         (void) strcpy(strippath, REPOS_STRIP);
  301.     if (strncmp(rcs, strippath, strlen(strippath)) == 0)
  302.         rcs += strlen(strippath);
  303.     *rindex(rcs, ',') = '\0';
  304.     if (vers_tag[0] != '\0') {
  305.         (void) sprintf(file1, "%s%s%s:%s", update_dir,
  306.                update_dir[0] ? DIRSEPSTR : "", rcs, vers_tag);
  307.     } else {
  308.         (void) strcpy(file1, DEVNULL);
  309.     }
  310.         (void) sprintf(file2, "%s%s%s:%s", update_dir,
  311.                 update_dir[0] ? DIRSEPSTR : "", rcs, vers_head);
  312.     printf("diff -c %s %s\n", file1, file2);
  313.     printf("*** %s%s--- ", file1, cp1);
  314.     /* but these dates pointed to by cp1 and cp2 aren't very useful ?!? */
  315.     if (update_dir[0] != '\0')
  316.         printf("%s%c", update_dir, DIRSEP);
  317.     printf("%s%s", rcs, cp2);
  318.     while (fgets(line1, sizeof(line1), fp) != NULL)
  319.         printf("%s", line1);
  320.     (void) fclose(fp);
  321.     }
  322. out:
  323.     (void) unlink(tmpfile1);
  324.     (void) unlink(tmpfile2);
  325.     (void) unlink(tmpfile3);
  326.     return (ret);
  327. }
  328.  
  329. void
  330. patch_cleanup(sig)
  331.     int sig;
  332. {
  333.     if (tmpfile1[0] != '\0')
  334.     (void) unlink(tmpfile1);
  335.     if (tmpfile2[0] != '\0')
  336.     (void) unlink(tmpfile2);
  337.     if (tmpfile3[0] != '\0')
  338.     (void) unlink(tmpfile3);
  339.     if (sig != 0)
  340.     exit(1);
  341. }
  342.  
  343. /*
  344.  * Lookup the specified revision in the ,v file and return, in the date
  345.  * argument, the date specified for the revision *minus one second*,
  346.  * so that the logically previous revision will be foun by Version_Number()
  347.  * later.
  348.  *
  349.  * Returns zero on success, non-zero on failure.
  350.  */
  351. static
  352. get_rcsdate(rcs, rev, date)
  353.     char *rcs;
  354.     char *rev;
  355.     char *date;
  356. {
  357.     char line[MAXLINELEN];
  358.     struct tm tm, *ftm;
  359.     char *cp, *semi;
  360.     time_t revdate;
  361.     FILE *fp;
  362.     int ret = 1;
  363.  
  364.     if ((fp = fopen(rcs, "r")) == NULL)
  365.     return (1);
  366.     while (fgets(line, sizeof(line), fp) != NULL) {
  367.     if (!isdigit(line[0]))
  368.         continue;
  369.     if ((cp = rindex(line, '\n')) != NULL)
  370.         *cp = '\0';
  371.     if (strcmp(line, rev) == 0) {
  372.         if (fgets(line, sizeof(line), fp) == NULL)
  373.         break;
  374.         for (cp = line; *cp && !isspace(*cp); cp++)
  375.         ;
  376.         while (*cp && isspace(*cp))
  377.         cp++;
  378.         if (*cp && (semi = index(cp, ';')) != NULL) {
  379.         ret = 0;
  380.         *semi = '\0';
  381.         semi[-1]--;    /* This works even if semi[-1] was '0'.  */
  382.         }
  383.         break;
  384.     }
  385.     }
  386.     (void) fclose(fp);
  387.     strcpy(date, cp);
  388.     return (ret);
  389. }
  390.  
  391. static
  392. patch_usage()
  393. {
  394.     (void) fprintf(stderr,
  395.     "%s %s [-Qqlf] [-s|-t] [-r tag|-D date [-r tag|-D date]] modules...\n",
  396.            progname, command);
  397.     exit(1);
  398. }
  399.