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

  1. #ifndef lint
  2. static char rcsid[] = "$Id: version_number.c,v 1.16.1.2 91/01/29 07:20:26 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.  * Version Number
  12.  *
  13.  *    Returns the requested version number of the RCS file, satisfying tags
  14.  *    and walking branches, if necessary.
  15.  *
  16.  *    "rcs" is the full pathname to the ,v file which is read to determine
  17.  *    the current head version number for the RCS file.  Things are
  18.  *    complicated by needing to walk branches and symbolic tags.  The
  19.  *    algorithm used here for branches is not quite the same as that
  20.  *    included with RCS, but should work 99.999% of the time (or more).
  21.  *
  22.  *    The result is placed in "vers"; null-string if error.
  23.  */
  24.  
  25. #include <ctype.h>
  26. #include "cvs.h"
  27.  
  28. Version_Number(rcs, tag, date, vers)
  29.     char *rcs;
  30.     char *tag;
  31.     char *date;
  32.     char *vers;
  33. {
  34.     FILE *fpin;
  35.     char rev[50];
  36.  
  37.     vers[0] = rev[0] = '\0';        /* Assume failure */
  38.     /*
  39.      * Open the RCS file.  If it failed, just return, as the version
  40.      * is already nulled.
  41.      */
  42.     if ((fpin = fopen(rcs, "r")) == NULL)
  43.     return 0;
  44.     get_version(fpin, tag, date, rev, vers);
  45.     if (vers[0] == '\0' && tag[0] != '\0') {
  46.     if (!isdigit(tag[0])) {
  47.         char *cp, temp[MAXLINELEN];
  48.         extern char update_dir[];
  49.  
  50.         if (CVSroot[0] != '\0' &&
  51.         strncmp(rcs, CVSroot, strlen(CVSroot)) == 0) {
  52.         cp = rcs + strlen(CVSroot) + 1;
  53.         } else {
  54.         if ((cp = index_sep(rcs)) == NULL && update_dir[0] != '\0') {
  55.             (void) sprintf(temp, "%s%c%s", update_dir, DIRSEP, rcs);
  56.             cp = temp;
  57.         } else {
  58.             cp = rcs;
  59.         }
  60.         }
  61.         if (force_tag_match) {
  62.         if (!quiet)
  63.             warn(0, "tag %s undefined in %s; ignored", tag, cp);
  64.         } else {
  65.         if (!quiet)
  66.             warn(0, "tag %s undefined in %s; using %s", tag, cp, rev);
  67.         (void) strcpy(vers, rev);
  68.         }
  69.     } else {
  70.         if (!force_tag_match)
  71.         (void) strcpy(vers, rev);
  72.     }
  73.     }
  74.     (void) fclose(fpin);
  75. }
  76.  
  77. /*
  78.  * The main driver for doing the work of getting the required revision
  79.  * number.  Returns computed revision in "rev" or "vers" depending
  80.  * on whether the "tag" could be satisfied or not.
  81.  */
  82. static
  83. get_version(fp, tag, date, rev, vers)
  84.     FILE *fp;
  85.     char *tag;
  86.     char *date;
  87.     char *rev;
  88.     char *vers;
  89. {
  90.     char line[MAXLINELEN];
  91.     char *cp;
  92.     int symtag_matched = 0;
  93.  
  94.     /*
  95.      * Scan to find the current "head" setting,
  96.      * which really isn't the head if the RCS file is using a branch
  97.      * for the head, sigh.
  98.      *
  99.      * Assumption here is that the "head" line is always first.
  100.      */
  101.     if (fgets(line, sizeof(line), fp) == NULL)
  102.     return 0;
  103.     if (strncmp(line, RCSHEAD, sizeof(RCSHEAD) - 1) != 0 ||
  104.     !isspace(line[sizeof(RCSHEAD) - 1]) ||
  105.     (cp = rindex(line, ';')) == NULL)
  106.     return 0;
  107.     *cp = '\0';                /* strip the ';' */
  108.     if ((cp = rindex(line, ' ')) == NULL &&
  109.     (cp = rindex(line, '\t')) == NULL)
  110.     return 0;
  111.     cp++;
  112.     (void) strcpy(rev, cp);
  113.     /*
  114.      * The "rev" string now contains the value of the RCS "head".
  115.      * Read the next line to find out if we should walk the branches.
  116.      *
  117.      * Assumption here is that "branch" is always on the second line
  118.      * of the RCS file.  If a "branch" line does not exist, we assume
  119.      * it is an old format RCS file, and blow it off.
  120.      */
  121.     if (fgets(line, sizeof(line), fp) == NULL)
  122.     return 0;
  123.     if (strncmp(line, RCSBRANCH, sizeof(RCSBRANCH) - 1) == 0 &&
  124.     isspace(line[sizeof(RCSBRANCH) - 1]) &&
  125.     (cp = rindex(line, ';')) != NULL) {
  126.     *cp = '\0';            /* strip the ';' */
  127.     if ((cp = rindex(line, ' ')) == NULL &&
  128.         (cp = rindex(line, '\t')) == NULL)
  129.         return 0;
  130.     cp++;
  131.     if (*cp != 0)
  132.         (void) strcpy(rev, cp);
  133.     }
  134.     /*
  135.      * "rev" now contains either the "head" value, or the "branch"
  136.      * value (if it was set).  If we're looking for a particular symbolic
  137.      * or numeric tag, we must find the symbol, and then do
  138.      * branch completion as usual, if necessary.
  139.      */
  140.     if (date[0] != '\0') {
  141.     get_date(fp, date, rev, vers);
  142.     return 0;
  143.     }
  144.     if (tag[0] != '\0') {
  145.     /* return of 0 means we found an exact match, or there was an error */
  146.     if ((symtag_matched = get_tag(fp, tag, rev, vers)) == 0)
  147.         return 0;
  148.     }
  149.     /*
  150.      * "rev" now contains either the "head" value, or the tag value,
  151.      * or the "branch" value.  get_branch() will fill in "rev" with
  152.      * the highest numbered branch off "rev", if necessary.
  153.      */
  154.     get_branch(fp, rev);
  155.     if (tag[0] == '\0' || isdigit(tag[0]) || symtag_matched < 0) {
  156.     if ((numdots(rev) & 1) != 0)
  157.         (void) strcpy(vers, rev);
  158.     }
  159. }
  160.  
  161. /*
  162.  * We were requested to find a particular symbolic or numeric revision.
  163.  * So, scan for the "symbols" line in the RCS file, or for the first
  164.  * match of a line if the tag is numeric.
  165.  *
  166.  * Would really like to use strtok() here, but callers in update() are
  167.  * already using it.
  168.  *
  169.  * Return value of 0 means to use "vers" as it stands, while a return value
  170.  * of 1 means to use "rev", but to check for possible branch completions
  171.  * to find the head of a branch.
  172.  */
  173. static
  174. get_tag(fp, tag, rev, vers)
  175.     FILE *fp;
  176.     char *tag;
  177.     char *rev;
  178.     char *vers;
  179. {
  180.     char line[MAXLINELEN];
  181.     char *cp, *cprev;
  182.  
  183.     if (isdigit(tag[0])) {
  184.     while (tag[strlen(tag)-1] == '.')
  185.         tag[strlen(tag)-1] = '\0';    /* strip trailing dots */
  186.     if ((numdots(tag) & 1) == 0) {
  187.         (void) strcpy(rev, tag);
  188.         return (1);            /* let get_branch() figure it out */
  189.     }
  190.     }
  191.     while (fgets(line, sizeof(line), fp) != NULL) {
  192.     if (strncmp(line, RCSDESC, sizeof(RCSDESC) - 1) == 0) {
  193.         rewind(fp);
  194.         return (1);            /* use head */
  195.     }
  196.     /*
  197.      * For numeric tags, the RCS file contains the revision
  198.      * number all by itself on a single line, so we check for
  199.      * that match here.
  200.      */
  201.     if (isdigit(tag[0])) {
  202.         if ((cp = rindex(line, '\n')) != NULL)
  203.         *cp = '\0';
  204.         if (strcmp(tag, line) == 0) {
  205.         (void) strcpy(vers, line);
  206.         return (0);        /* a match for a numeric tag */
  207.         }
  208.         continue;
  209.     }
  210.     if (strncmp(line, RCSSYMBOL, sizeof(RCSSYMBOL)-1)!=0 ||
  211.         !isspace(line[sizeof(RCSSYMBOL) - 1]))
  212.         continue;
  213.     /*
  214.      * A rather ugly loop to process the "symbols" line.  I would
  215.      * really rather use strtok(), but other above me already are,
  216.      * and strtok() blows up in this case.
  217.      */
  218.     for (cp = line + sizeof(RCSSYMBOL);  cp;  ) {
  219.         while (isspace(*cp))
  220.         cp++;
  221.         if (*cp == ';')
  222.         break;
  223.         if (!*cp) {
  224.             if (fgets(line, sizeof(line), fp) == NULL)
  225.             return 0;
  226.         cp = line;
  227.         continue;
  228.         }
  229.         /* symbols and revisions are separated by a colon */
  230.         if ((cprev = index(cp, ':')) == NULL) {
  231.         while (*cp && !isspace(*cp) && *cp!=';')
  232.             cp++;
  233.         continue;
  234.         }
  235.         *cprev++ = '\0';
  236.         /*
  237.          * "cp" points to the NULL-terminated symbolic name;
  238.          * "cprev" points to the revision, which must be NULL-terminated;
  239.          */
  240.         if (strcmp(tag, cp) == 0) {
  241.         if ((cp = index(cprev, ' ')) == NULL
  242.             && (cp = index(cprev, ';')) == NULL
  243.             && (cp = index(cprev, '\n')) == NULL)
  244.             continue;
  245.         *cp = '\0';
  246.         (void) strcpy(rev, cprev);
  247.         return (-1);        /* look for branches off rev */
  248.         } else {
  249.         while (!isspace(*cp) && *cp!=';')
  250.             cp++;
  251.         }
  252.     }
  253.     return (1);
  254.     }
  255.     return (0);
  256. }
  257.  
  258. /*
  259.  * Decides if we should determine the highest branch number, and
  260.  * returns it in "rev".  This is only done if there are
  261.  * an even number of dots ('.') in the revision number passed in.
  262.  */
  263. static
  264. get_branch(fp, rev)
  265.     FILE *fp;
  266.     char *rev;
  267. {
  268.     char line[MAXLINELEN];
  269.     char branch[50];
  270.     char *cp;
  271.     int len, dots = numdots(rev);
  272.  
  273.     if ((dots & 1) != 0)
  274.     return 0;
  275.     (void) sprintf(branch, "%s.", rev);
  276.     len = strlen(branch);
  277.     while (fgets(line, sizeof(line), fp) != NULL) {
  278.     if (strncmp(line, RCSDESC, sizeof(RCSDESC) - 1) == 0)
  279.         return 0;
  280.     if (isdigit(line[0])) {
  281.         if ((cp = rindex(line, '\n')) != NULL)
  282.         *cp = '\0';        /* strip the newline */
  283.         if (numdots(line) == dots+1 &&
  284.         strncmp(line, branch, len) == 0) {
  285.         if ((numdots(rev) & 1) == 0 || strcmp(rev, line) <= 0)
  286.             (void) strcpy(rev, line);
  287.         }
  288.     }
  289.     }
  290. }
  291.  
  292. /*
  293.  * Look up a the most recent revision, based on the supplied date.
  294.  * But do some funky stuff if necessary to follow any vendor branch.
  295.  */
  296. static
  297. get_date(fp, date, rev, version)
  298.     FILE *fp;
  299.     char *date;
  300.     char *rev;
  301.     char *version;
  302. {
  303.     char *cp;
  304.  
  305.     if ((numdots(rev) & 1) == 0) {
  306.     /*
  307.      * A branch is the head, so get the revision from the branch
  308.      * specified in "rev".  If we didn't get a match, try the trunk.
  309.      */
  310.     get_branch_date(fp, date, rev, version);
  311.     if (version[0] == '\0') {
  312.         if ((cp = index(rev, '.')) != NULL)
  313.         *cp = '\0';
  314.         rewind(fp);
  315.         get_branch_date(fp, date, rev, version);
  316.     }
  317.     } else {
  318.     /*
  319.      * The trunk is the head.  Get the revision from the trunk and
  320.      * see if it evaluates to 1.1.  If so, walk the 1.1.1 branch looking
  321.      * for a match and return that; if not, just return 1.1.
  322.      */
  323.     if ((cp = rindex(rev, '.')) != NULL)
  324.         *cp = '\0';            /* turn the revision into a branch */
  325.     get_branch_date(fp, date, rev, version);
  326.     if (strcmp(version, "1.1") == 0) {
  327.         rewind(fp);
  328.         get_branch_date(fp, date, "1.1.1", version);
  329.     }
  330.     }
  331. }
  332.  
  333. static
  334. get_branch_date(fp, date, rev, version)
  335.     FILE *fp;
  336.     char *date;
  337.     char *rev;
  338.     char *version;
  339. {
  340.     char line[MAXLINELEN], last_rev[50], curdate[50], date_dot[50];
  341.     int date_dots, date_dotlen;
  342.     char *cp, *semi;
  343.  
  344.     last_rev[0] = '\0';
  345.     curdate[0] = '\0';
  346.     (void) sprintf(date_dot, "%s.", rev);
  347.     date_dotlen = strlen(date_dot);
  348.     date_dots = numdots(rev);
  349.     while (fgets(line, sizeof(line), fp) != NULL) {
  350.     if (strncmp(line, RCSDESC, sizeof(RCSDESC) - 1) == 0)
  351.         return 0;
  352.     if (isdigit(line[0])) {
  353.         if ((cp = rindex(line, '\n')) != NULL)
  354.         *cp = '\0';        /* strip the newline */
  355.         if ((date_dots == 0 || strncmp(date_dot, line, date_dotlen) == 0) &&
  356.         numdots(line) == date_dots+1)
  357.         (void) strcpy(last_rev, line);
  358.         else
  359.         last_rev[0] = '\0';
  360.         continue;
  361.     }
  362.     if (strncmp(line, RCSDATE, sizeof(RCSDATE) - 1) == 0 &&
  363.         isspace(line[sizeof(RCSDATE) - 1]) &&
  364.         last_rev[0] != '\0') {
  365.         for (cp = line; *cp && !isspace(*cp); cp++)
  366.         ;
  367.         while (*cp && isspace(*cp))
  368.         cp++;
  369.         if (*cp && (semi = index(cp, ';')) != NULL) {
  370.         *semi = '\0';        /* strip the semicolon */
  371.         if (datecmp(cp, date) <= 0 && datecmp(cp, curdate) >= 0) {
  372.             (void) strcpy(curdate, cp);
  373.             (void) strcpy(version, last_rev);
  374.         }
  375.         }
  376.     }
  377.     }
  378. }
  379.  
  380. /*
  381.  * Compare two dates in RCS format.
  382.  * Beware the change in format on January 1, 2000,
  383.  * when years go from 2-digit to full format.
  384.  */
  385. int
  386. datecmp(date1, date2)
  387.     char *date1, *date2;
  388. {
  389.     int length_diff = strlen(date1) - strlen(date2);
  390.     return length_diff ? length_diff : strcmp(date1, date2);
  391. }
  392.  
  393.