summaryrefslogtreecommitdiff
path: root/usr.bin/rcs/rcsnum.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/rcs/rcsnum.c')
-rw-r--r--usr.bin/rcs/rcsnum.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/usr.bin/rcs/rcsnum.c b/usr.bin/rcs/rcsnum.c
new file mode 100644
index 00000000000..1c6aa5af620
--- /dev/null
+++ b/usr.bin/rcs/rcsnum.c
@@ -0,0 +1,398 @@
+/* $OpenBSD: rcsnum.c,v 1.1 2006/04/26 02:55:13 joris Exp $ */
+/*
+ * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#include "rcs.h"
+#include "xmalloc.h"
+
+static void rcsnum_setsize(RCSNUM *, u_int);
+static char *rcsnum_itoa(u_int16_t, char *, size_t);
+
+int rcsnum_flags;
+
+/*
+ * rcsnum_alloc()
+ *
+ * Allocate an RCS number structure and return a pointer to it.
+ */
+RCSNUM *
+rcsnum_alloc(void)
+{
+ RCSNUM *rnp;
+
+ rnp = xmalloc(sizeof(*rnp));
+ rnp->rn_len = 0;
+ rnp->rn_id = NULL;
+
+ return (rnp);
+}
+
+/*
+ * rcsnum_parse()
+ *
+ * Parse a string specifying an RCS number and return the corresponding RCSNUM.
+ */
+RCSNUM *
+rcsnum_parse(const char *str)
+{
+ char *ep;
+ RCSNUM *num;
+
+ num = rcsnum_alloc();
+ if (rcsnum_aton(str, &ep, num) < 0 || *ep != '\0') {
+ rcsnum_free(num);
+ num = NULL;
+ if (*ep != '\0')
+ rcs_errno = RCS_ERR_BADNUM;
+ }
+
+ return (num);
+}
+
+/*
+ * rcsnum_free()
+ *
+ * Free an RCSNUM structure previously allocated with rcsnum_alloc().
+ */
+void
+rcsnum_free(RCSNUM *rn)
+{
+ if (rn->rn_id != NULL)
+ xfree(rn->rn_id);
+ xfree(rn);
+}
+
+/*
+ * rcsnum_tostr()
+ *
+ * Format the RCS number <nump> into a human-readable dot-separated
+ * representation and store the resulting string in <buf>, which is of size
+ * <blen>.
+ * Returns a pointer to the start of <buf>. On failure <buf> is set to
+ * an empty string.
+ */
+char *
+rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen)
+{
+ u_int i;
+ char tmp[8];
+
+ if (nump == NULL || nump->rn_len == 0) {
+ buf[0] = '\0';
+ return (buf);
+ }
+
+ strlcpy(buf, rcsnum_itoa(nump->rn_id[0], buf, blen), blen);
+ for (i = 1; i < nump->rn_len; i++) {
+ strlcat(buf, ".", blen);
+ strlcat(buf, rcsnum_itoa(nump->rn_id[i], tmp, sizeof(tmp)),
+ blen);
+ }
+
+ return (buf);
+}
+
+static char *
+rcsnum_itoa(u_int16_t num, char *buf, size_t len)
+{
+ u_int16_t i;
+ char *p;
+
+ if (num == 0)
+ return "0";
+
+ p = buf + len - 1;
+ i = num;
+ bzero(buf, len);
+ while (i) {
+ *--p = '0' + (i % 10);
+ i /= 10;
+ }
+ return (p);
+}
+
+/*
+ * rcsnum_cpy()
+ *
+ * Copy the number stored in <nsrc> in the destination <ndst> up to <depth>
+ * numbers deep. If <depth> is 0, there is no depth limit.
+ */
+void
+rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth)
+{
+ u_int len;
+ void *tmp;
+
+ len = nsrc->rn_len;
+ if (depth != 0 && len > depth)
+ len = depth;
+
+ tmp = xrealloc(ndst->rn_id, len, sizeof(len));
+ ndst->rn_id = tmp;
+ ndst->rn_len = len;
+ /* Overflow checked in xrealloc(). */
+ (void)memcpy(ndst->rn_id, nsrc->rn_id, len * sizeof(len));
+}
+
+/*
+ * rcsnum_cmp()
+ *
+ * Compare the two numbers <n1> and <n2>. Returns -1 if <n1> is larger than
+ * <n2>, 0 if they are both the same, and 1 if <n2> is larger than <n1>.
+ * The <depth> argument specifies how many numbers deep should be checked for
+ * the result. A value of 0 means that the depth will be the minimum of the
+ * two numbers.
+ */
+int
+rcsnum_cmp(const RCSNUM *n1, const RCSNUM *n2, u_int depth)
+{
+ int res;
+ u_int i;
+ size_t slen;
+
+ slen = MIN(n1->rn_len, n2->rn_len);
+ if (depth != 0 && slen > depth)
+ slen = depth;
+
+ for (i = 0; i < slen; i++) {
+ res = n1->rn_id[i] - n2->rn_id[i];
+ if (res < 0)
+ return (1);
+ else if (res > 0)
+ return (-1);
+ }
+
+ if (n1->rn_len > n2->rn_len)
+ return (-1);
+ else if (n2->rn_len > n1->rn_len)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * rcsnum_aton()
+ *
+ * Translate the string <str> containing a sequence of digits and periods into
+ * its binary representation, which is stored in <nump>. The address of the
+ * first byte not part of the number is stored in <ep> on return, if it is not
+ * NULL.
+ * Returns 0 on success, or -1 on failure.
+ */
+int
+rcsnum_aton(const char *str, char **ep, RCSNUM *nump)
+{
+ u_int32_t val;
+ const char *sp;
+ void *tmp;
+ char *s;
+
+ if (nump->rn_id == NULL)
+ nump->rn_id = xmalloc(sizeof(*(nump->rn_id)));
+
+ nump->rn_len = 0;
+ nump->rn_id[0] = 0;
+
+ for (sp = str;; sp++) {
+ if (!isdigit(*sp) && (*sp != '.'))
+ break;
+
+ if (*sp == '.') {
+ if (nump->rn_len >= RCSNUM_MAXLEN - 1) {
+ rcs_errno = RCS_ERR_BADNUM;
+ goto rcsnum_aton_failed;
+ }
+
+ nump->rn_len++;
+ tmp = xrealloc(nump->rn_id,
+ nump->rn_len + 1, sizeof(*(nump->rn_id)));
+ nump->rn_id = tmp;
+ nump->rn_id[nump->rn_len] = 0;
+ continue;
+ }
+
+ val = (nump->rn_id[nump->rn_len] * 10) + (*sp - 0x30);
+ if (val > RCSNUM_MAXNUM)
+ errx(1, "RCSNUM overflow!");
+
+ nump->rn_id[nump->rn_len] = val;
+ }
+
+ if (ep != NULL)
+ *(const char **)ep = sp;
+
+ /*
+ * Handle "magic" RCS branch numbers.
+ *
+ * What are they?
+ *
+ * Magic branch numbers have an extra .0. at the second farmost
+ * rightside of the branch number, so instead of having an odd
+ * number of dot-separated decimals, it will have an even number.
+ *
+ * Now, according to all the documentation i've found on the net
+ * about this, cvs does this for "efficiency reasons", i'd like
+ * to hear one.
+ *
+ * We just make sure we remove the .0. from in the branch number.
+ *
+ * XXX - for compatibility reasons with GNU cvs we _need_
+ * to skip this part for the 'log' command, apparently it does
+ * show the magic branches for an unknown and probably
+ * completely insane and not understandable reason in that output.
+ *
+ */
+ if (nump->rn_len > 2 && nump->rn_id[nump->rn_len - 1] == 0
+ && !(rcsnum_flags & RCSNUM_NO_MAGIC)) {
+ /*
+ * Look for ".0.x" at the end of the branch number.
+ */
+ if ((s = strrchr(str, '.')) != NULL) {
+ s--;
+ while (*s != '.')
+ s--;
+
+ /*
+ * If we have a "magic" branch, adjust it
+ * so the .0. is removed.
+ */
+ if (!strncmp(s, RCS_MAGIC_BRANCH,
+ strlen(RCS_MAGIC_BRANCH))) {
+ nump->rn_id[nump->rn_len - 1] =
+ nump->rn_id[nump->rn_len];
+ nump->rn_len--;
+ }
+ }
+ }
+
+ /* We can't have a single-digit rcs number. */
+ if (nump->rn_len == 0) {
+ tmp = xrealloc(nump->rn_id,
+ nump->rn_len + 1, sizeof(*(nump->rn_id)));
+ nump->rn_id = tmp;
+ nump->rn_id[nump->rn_len + 1] = 0;
+ nump->rn_len++;
+ }
+
+ nump->rn_len++;
+ return (nump->rn_len);
+
+rcsnum_aton_failed:
+ nump->rn_len = 0;
+ xfree(nump->rn_id);
+ nump->rn_id = NULL;
+ return (-1);
+}
+
+/*
+ * rcsnum_inc()
+ *
+ * Increment the revision number specified in <num>.
+ * Returns a pointer to the <num> on success, or NULL on failure.
+ */
+RCSNUM *
+rcsnum_inc(RCSNUM *num)
+{
+ if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM)
+ return (NULL);
+ num->rn_id[num->rn_len - 1]++;
+ return (num);
+}
+
+/*
+ * rcsnum_dec()
+ *
+ * Decreases the revision number specified in <num>, if doing so will not
+ * result in an ending value below 1. E.g. 4.2 will go to 4.1 but 4.1 will
+ * be returned as 4.1.
+ */
+RCSNUM *
+rcsnum_dec(RCSNUM *num)
+{
+ /* XXX - Is it an error for the number to be 0? */
+ if (num->rn_id[num->rn_len - 1] <= 1)
+ return (num);
+ num->rn_id[num->rn_len - 1]--;
+ return (num);
+}
+
+/*
+ * rcsnum_revtobr()
+ *
+ * Retrieve the branch number associated with the revision number <num>.
+ * If <num> is a branch revision, the returned value will be the same
+ * number as the argument.
+ */
+RCSNUM *
+rcsnum_revtobr(const RCSNUM *num)
+{
+ RCSNUM *brnum;
+
+ if (num->rn_len < 2)
+ return (NULL);
+
+ brnum = rcsnum_alloc();
+ rcsnum_cpy(num, brnum, 0);
+
+ if (!RCSNUM_ISBRANCH(brnum))
+ brnum->rn_len--;
+
+ return (brnum);
+}
+
+/*
+ * rcsnum_brtorev()
+ *
+ * Retrieve the initial revision number associated with the branch number <num>.
+ * If <num> is a revision number, an error will be returned.
+ */
+RCSNUM *
+rcsnum_brtorev(const RCSNUM *brnum)
+{
+ RCSNUM *num;
+
+ if (!RCSNUM_ISBRANCH(brnum)) {
+ return (NULL);
+ }
+
+ num = rcsnum_alloc();
+ rcsnum_setsize(num, brnum->rn_len + 1);
+ rcsnum_cpy(brnum, num, brnum->rn_len);
+ num->rn_id[num->rn_len++] = 1;
+
+ return (num);
+}
+
+static void
+rcsnum_setsize(RCSNUM *num, u_int len)
+{
+ void *tmp;
+
+ tmp = xrealloc(num->rn_id, len, sizeof(*(num->rn_id)));
+ num->rn_id = tmp;
+ num->rn_len = len;
+}