xref: /openbsd-src/usr.bin/cvs/rcsnum.c (revision 0fcddf58fdf3a3d288eec871c6010f19e4d8c8e3)
1*0fcddf58Sotto /*	$OpenBSD: rcsnum.c,v 1.59 2017/08/28 19:33:20 otto Exp $	*/
26c121f58Sjfb /*
36c121f58Sjfb  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
46c121f58Sjfb  * All rights reserved.
56c121f58Sjfb  *
66c121f58Sjfb  * Redistribution and use in source and binary forms, with or without
76c121f58Sjfb  * modification, are permitted provided that the following conditions
86c121f58Sjfb  * are met:
96c121f58Sjfb  *
106c121f58Sjfb  * 1. Redistributions of source code must retain the above copyright
116c121f58Sjfb  *    notice, this list of conditions and the following disclaimer.
126c121f58Sjfb  * 2. The name of the author may not be used to endorse or promote products
136c121f58Sjfb  *    derived from this software without specific prior written permission.
146c121f58Sjfb  *
156c121f58Sjfb  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
166c121f58Sjfb  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
176c121f58Sjfb  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
186c121f58Sjfb  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
196c121f58Sjfb  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
206c121f58Sjfb  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
216c121f58Sjfb  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
226c121f58Sjfb  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
236c121f58Sjfb  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
246c121f58Sjfb  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
256c121f58Sjfb  */
266c121f58Sjfb 
271f8531bdSotto #include <ctype.h>
28397ddb8aSnicm #include <stdlib.h>
291f8531bdSotto #include <string.h>
306c121f58Sjfb 
31b64c20faSjoris #include "cvs.h"
326c121f58Sjfb 
33b9fc9a72Sderaadt #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
34b9fc9a72Sderaadt 
3546fd924fSxsa static void	 rcsnum_setsize(RCSNUM *, u_int);
365dea531bSniallo static char	*rcsnum_itoa(u_int16_t, char *, size_t);
372043120eSjfb 
386c121f58Sjfb /*
396c121f58Sjfb  * rcsnum_alloc()
406c121f58Sjfb  *
410f9a9ee2Sray  * Allocate an RCS number structure and return a pointer to it.
426c121f58Sjfb  */
436c121f58Sjfb RCSNUM *
rcsnum_alloc(void)446c121f58Sjfb rcsnum_alloc(void)
456c121f58Sjfb {
466c121f58Sjfb 	RCSNUM *rnp;
476c121f58Sjfb 
488fe0da22Stedu 	rnp = xcalloc(1, sizeof(*rnp));
496c121f58Sjfb 	rnp->rn_len = 0;
506c121f58Sjfb 
516c121f58Sjfb 	return (rnp);
526c121f58Sjfb }
536c121f58Sjfb 
546c121f58Sjfb /*
550f301d36Stobias  * rcsnum_addmagic()
560f301d36Stobias  *
570f301d36Stobias  * Adds a magic branch number to an RCS number.
580f301d36Stobias  * Returns 0 on success, or -1 on failure.
590f301d36Stobias  */
600f301d36Stobias int
rcsnum_addmagic(RCSNUM * rn)610f301d36Stobias rcsnum_addmagic(RCSNUM *rn)
620f301d36Stobias {
630f301d36Stobias 	if (!rn->rn_len || rn->rn_len > RCSNUM_MAXLEN - 1)
640f301d36Stobias 		return -1;
650f301d36Stobias 	rcsnum_setsize(rn, rn->rn_len + 1);
660f301d36Stobias 	rn->rn_id[rn->rn_len - 1] = rn->rn_id[rn->rn_len - 2];
670f301d36Stobias 	rn->rn_id[rn->rn_len - 2] = 0;
680f301d36Stobias 
690f301d36Stobias 	return 0;
700f301d36Stobias }
710f301d36Stobias 
720f301d36Stobias /*
7317d257bdSjfb  * rcsnum_parse()
7417d257bdSjfb  *
7517d257bdSjfb  * Parse a string specifying an RCS number and return the corresponding RCSNUM.
7617d257bdSjfb  */
7717d257bdSjfb RCSNUM *
rcsnum_parse(const char * str)7817d257bdSjfb rcsnum_parse(const char *str)
7917d257bdSjfb {
80*0fcddf58Sotto 	const char *ep;
8117d257bdSjfb 	RCSNUM *num;
8217d257bdSjfb 
833422a189Sjoris 	num = rcsnum_alloc();
84d593696fSderaadt 	if (rcsnum_aton(str, &ep, num) < 0 || *ep != '\0') {
8553ce2177Sfcambus 		free(num);
864580e1eeSjfb 		num = NULL;
8717d257bdSjfb 	}
8817d257bdSjfb 
8917d257bdSjfb 	return (num);
9017d257bdSjfb }
9117d257bdSjfb 
9217d257bdSjfb /*
936c121f58Sjfb  * rcsnum_tostr()
944cc49e09Sjfb  *
954cc49e09Sjfb  * Format the RCS number <nump> into a human-readable dot-separated
964cc49e09Sjfb  * representation and store the resulting string in <buf>, which is of size
974cc49e09Sjfb  * <blen>.
98d3b0e455Sray  * Returns a pointer to the start of <buf>.  On failure <buf> is set to
99d3b0e455Sray  * an empty string.
1006c121f58Sjfb  */
1016c121f58Sjfb char *
rcsnum_tostr(const RCSNUM * nump,char * buf,size_t blen)1026c121f58Sjfb rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen)
1036c121f58Sjfb {
1046c121f58Sjfb 	u_int i;
1056c121f58Sjfb 	char tmp[8];
1066c121f58Sjfb 
107d593696fSderaadt 	if (nump == NULL || nump->rn_len == 0) {
1086c121f58Sjfb 		buf[0] = '\0';
1096c121f58Sjfb 		return (buf);
1106c121f58Sjfb 	}
1116c121f58Sjfb 
112754f5603Sxsa 	if (strlcpy(buf, rcsnum_itoa(nump->rn_id[0], buf, blen), blen) >= blen)
113754f5603Sxsa 		fatal("rcsnum_tostr: truncation");
1146c121f58Sjfb 	for (i = 1; i < nump->rn_len; i++) {
115754f5603Sxsa 		const char *str;
1163ad3fb45Sjoris 
117754f5603Sxsa 		str = rcsnum_itoa(nump->rn_id[i], tmp, sizeof(tmp));
118754f5603Sxsa 		if (strlcat(buf, ".", blen) >= blen ||
119754f5603Sxsa 		    strlcat(buf, str, blen) >= blen)
120754f5603Sxsa 			fatal("rcsnum_tostr: truncation");
1216c121f58Sjfb 	}
1226c121f58Sjfb 	return (buf);
1236c121f58Sjfb }
1246c121f58Sjfb 
1255dea531bSniallo static char *
rcsnum_itoa(u_int16_t num,char * buf,size_t len)1265dea531bSniallo rcsnum_itoa(u_int16_t num, char *buf, size_t len)
1275dea531bSniallo {
1285dea531bSniallo 	u_int16_t i;
1295dea531bSniallo 	char *p;
1305dea531bSniallo 
131b12ab1d2Sniallo 	if (num == 0)
132b12ab1d2Sniallo 		return "0";
133b12ab1d2Sniallo 
1345dea531bSniallo 	p = buf + len - 1;
1355dea531bSniallo 	i = num;
1365dea531bSniallo 	bzero(buf, len);
1375dea531bSniallo 	while (i) {
1385dea531bSniallo 		*--p = '0' + (i % 10);
1395dea531bSniallo 		i  /= 10;
1405dea531bSniallo 	}
1415dea531bSniallo 	return (p);
1425dea531bSniallo }
1435dea531bSniallo 
1446c121f58Sjfb /*
1456c121f58Sjfb  * rcsnum_cpy()
1466c121f58Sjfb  *
1476c121f58Sjfb  * Copy the number stored in <nsrc> in the destination <ndst> up to <depth>
1480f9a9ee2Sray  * numbers deep.  If <depth> is 0, there is no depth limit.
1496c121f58Sjfb  */
1500f9a9ee2Sray void
rcsnum_cpy(const RCSNUM * nsrc,RCSNUM * ndst,u_int depth)1516c121f58Sjfb rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth)
1526c121f58Sjfb {
1536c121f58Sjfb 	u_int len;
1546c121f58Sjfb 
1556c121f58Sjfb 	len = nsrc->rn_len;
156d593696fSderaadt 	if (depth != 0 && len > depth)
1576c121f58Sjfb 		len = depth;
1586c121f58Sjfb 
159cb0400d7Sray 	rcsnum_setsize(ndst, len);
1608fe0da22Stedu 	memcpy(ndst->rn_id, nsrc->rn_id, len * sizeof(*(nsrc->rn_id)));
1616c121f58Sjfb }
1626c121f58Sjfb 
1636c121f58Sjfb /*
1646c121f58Sjfb  * rcsnum_cmp()
1656c121f58Sjfb  *
1666c121f58Sjfb  * Compare the two numbers <n1> and <n2>. Returns -1 if <n1> is larger than
1676c121f58Sjfb  * <n2>, 0 if they are both the same, and 1 if <n2> is larger than <n1>.
1686c121f58Sjfb  * The <depth> argument specifies how many numbers deep should be checked for
16932128e2aSjoris  * the result.  A value of 0 means that the depth will be the maximum of the
17032128e2aSjoris  * two numbers, so that a longer number is considered greater than a shorter
17132128e2aSjoris  * number if they are equal up to the minimum length.
1726c121f58Sjfb  */
1736c121f58Sjfb int
rcsnum_cmp(RCSNUM * n1,RCSNUM * n2,u_int depth)174883d6377Sjoris rcsnum_cmp(RCSNUM *n1, RCSNUM *n2, u_int depth)
1756c121f58Sjfb {
1766c121f58Sjfb 	int res;
1776c121f58Sjfb 	u_int i;
1786c121f58Sjfb 	size_t slen;
1796c121f58Sjfb 
180883d6377Sjoris 	if (!rcsnum_differ(n1, n2))
181883d6377Sjoris 		return (0);
182883d6377Sjoris 
183b9fc9a72Sderaadt 	slen = MINIMUM(n1->rn_len, n2->rn_len);
184d593696fSderaadt 	if (depth != 0 && slen > depth)
1856c121f58Sjfb 		slen = depth;
1866c121f58Sjfb 
1876c121f58Sjfb 	for (i = 0; i < slen; i++) {
1886c121f58Sjfb 		res = n1->rn_id[i] - n2->rn_id[i];
1896c121f58Sjfb 		if (res < 0)
1906c121f58Sjfb 			return (1);
1916c121f58Sjfb 		else if (res > 0)
1926c121f58Sjfb 			return (-1);
1936c121f58Sjfb 	}
1946c121f58Sjfb 
19532128e2aSjoris 	/* If an explicit depth was specified, and we've
19632128e2aSjoris 	 * already checked up to depth, consider the
19732128e2aSjoris 	 * revision numbers equal. */
19832128e2aSjoris 	if (depth != 0 && slen == depth)
19932128e2aSjoris 		return (0);
20032128e2aSjoris 	else if (n1->rn_len > n2->rn_len)
2016c121f58Sjfb 		return (-1);
2026c121f58Sjfb 	else if (n2->rn_len > n1->rn_len)
2036c121f58Sjfb 		return (1);
2046c121f58Sjfb 
2056c121f58Sjfb 	return (0);
2066c121f58Sjfb }
2076c121f58Sjfb 
2086c121f58Sjfb /*
2096c121f58Sjfb  * rcsnum_aton()
2106c121f58Sjfb  *
2116c121f58Sjfb  * Translate the string <str> containing a sequence of digits and periods into
2126c121f58Sjfb  * its binary representation, which is stored in <nump>.  The address of the
2136c121f58Sjfb  * first byte not part of the number is stored in <ep> on return, if it is not
2146c121f58Sjfb  * NULL.
2156c121f58Sjfb  * Returns 0 on success, or -1 on failure.
2166c121f58Sjfb  */
2176c121f58Sjfb int
rcsnum_aton(const char * str,const char ** ep,RCSNUM * nump)218*0fcddf58Sotto rcsnum_aton(const char *str, const char **ep, RCSNUM *nump)
2196c121f58Sjfb {
2200b4202baSjfb 	u_int32_t val;
2216c121f58Sjfb 	const char *sp;
222b64c20faSjoris 	char *s;
2236c121f58Sjfb 
2246c121f58Sjfb 	nump->rn_len = 0;
2250b4202baSjfb 	nump->rn_id[0] = 0;
2266c121f58Sjfb 
2276c121f58Sjfb 	for (sp = str;; sp++) {
228f6ac027fSokan 		if (!isdigit((unsigned char)*sp) && (*sp != '.'))
2296c121f58Sjfb 			break;
2306c121f58Sjfb 
2316c121f58Sjfb 		if (*sp == '.') {
2325b95d21fSjoris 			if (nump->rn_len >= RCSNUM_MAXLEN - 1)
2330b4202baSjfb 				goto rcsnum_aton_failed;
2340b4202baSjfb 
2356c121f58Sjfb 			nump->rn_len++;
236e056a40dSvincent 			nump->rn_id[nump->rn_len] = 0;
2376c121f58Sjfb 			continue;
2386c121f58Sjfb 		}
2396c121f58Sjfb 
2404c9d22faSray 		val = (nump->rn_id[nump->rn_len] * 10) + (*sp - '0');
24187655548Sniallo 		if (val > RCSNUM_MAXNUM)
24287655548Sniallo 			fatal("RCSNUM overflow!");
2430b4202baSjfb 
2440b4202baSjfb 		nump->rn_id[nump->rn_len] = val;
2456c121f58Sjfb 	}
2466c121f58Sjfb 
2476c121f58Sjfb 	if (ep != NULL)
248*0fcddf58Sotto 		*ep = sp;
2496c121f58Sjfb 
250b64c20faSjoris 	/*
251b64c20faSjoris 	 * Handle "magic" RCS branch numbers.
252b64c20faSjoris 	 *
253b64c20faSjoris 	 * What are they?
254b64c20faSjoris 	 *
255b64c20faSjoris 	 * Magic branch numbers have an extra .0. at the second farmost
256b64c20faSjoris 	 * rightside of the branch number, so instead of having an odd
257b64c20faSjoris 	 * number of dot-separated decimals, it will have an even number.
258b64c20faSjoris 	 *
2598d7c5a97Skrw 	 * Now, according to all the documentation I've found on the net
2608d7c5a97Skrw 	 * about this, cvs does this for "efficiency reasons", I'd like
261b64c20faSjoris 	 * to hear one.
262b64c20faSjoris 	 *
263b64c20faSjoris 	 * We just make sure we remove the .0. from in the branch number.
264b64c20faSjoris 	 *
265b64c20faSjoris 	 * XXX - for compatibility reasons with GNU cvs we _need_
266b64c20faSjoris 	 * to skip this part for the 'log' command, apparently it does
267b64c20faSjoris 	 * show the magic branches for an unknown and probably
268b64c20faSjoris 	 * completely insane and not understandable reason in that output.
269b64c20faSjoris 	 *
270b64c20faSjoris 	 */
271a3fbd90cSjoris 	if (nump->rn_len > 2 && nump->rn_id[nump->rn_len - 1] == 0) {
272b64c20faSjoris 		/*
273b64c20faSjoris 		 * Look for ".0.x" at the end of the branch number.
274b64c20faSjoris 		 */
275b64c20faSjoris 		if ((s = strrchr(str, '.')) != NULL) {
2765920b252Sderaadt 			s--;
277b64c20faSjoris 			while (*s != '.')
2785920b252Sderaadt 				s--;
279b64c20faSjoris 
280b64c20faSjoris 			/*
281b64c20faSjoris 			 * If we have a "magic" branch, adjust it
282b64c20faSjoris 			 * so the .0. is removed.
283b64c20faSjoris 			 */
284b64c20faSjoris 			if (!strncmp(s, RCS_MAGIC_BRANCH,
285870158f9Stobias 			    sizeof(RCS_MAGIC_BRANCH) - 1)) {
286b64c20faSjoris 				nump->rn_id[nump->rn_len - 1] =
287b64c20faSjoris 				    nump->rn_id[nump->rn_len];
288b64c20faSjoris 				nump->rn_len--;
289b64c20faSjoris 			}
290b64c20faSjoris 		}
291b64c20faSjoris 	}
292b64c20faSjoris 
293b12ab1d2Sniallo 	/* We can't have a single-digit rcs number. */
294b12ab1d2Sniallo 	if (nump->rn_len == 0) {
295bd5aba4aStobias 		nump->rn_len++;
296bd5aba4aStobias 		nump->rn_id[nump->rn_len] = 0;
297b12ab1d2Sniallo 	}
298b1ce7b24Sjoris 
2996c121f58Sjfb 	nump->rn_len++;
3006c121f58Sjfb 	return (nump->rn_len);
3010b4202baSjfb 
3020b4202baSjfb rcsnum_aton_failed:
3030b4202baSjfb 	nump->rn_len = 0;
3040b4202baSjfb 	return (-1);
3056c121f58Sjfb }
3062043120eSjfb 
3072043120eSjfb /*
3082043120eSjfb  * rcsnum_inc()
3092043120eSjfb  *
3102043120eSjfb  * Increment the revision number specified in <num>.
3112043120eSjfb  * Returns a pointer to the <num> on success, or NULL on failure.
3122043120eSjfb  */
3132043120eSjfb RCSNUM *
rcsnum_inc(RCSNUM * num)3142043120eSjfb rcsnum_inc(RCSNUM *num)
3152043120eSjfb {
3162043120eSjfb 	if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM)
3172043120eSjfb 		return (NULL);
3182043120eSjfb 	num->rn_id[num->rn_len - 1]++;
3192043120eSjfb 	return (num);
3202043120eSjfb }
3212043120eSjfb 
3222043120eSjfb /*
3232c56d68bSjoris  * rcsnum_dec()
3242c56d68bSjoris  *
325b12ab1d2Sniallo  * Decreases the revision number specified in <num>, if doing so will not
326b12ab1d2Sniallo  * result in an ending value below 1. E.g. 4.2 will go to 4.1 but 4.1 will
327b12ab1d2Sniallo  * be returned as 4.1.
3282c56d68bSjoris  */
3292c56d68bSjoris RCSNUM *
rcsnum_dec(RCSNUM * num)3302c56d68bSjoris rcsnum_dec(RCSNUM *num)
3312c56d68bSjoris {
3324060a934Sray 	/* XXX - Is it an error for the number to be 0? */
3334060a934Sray 	if (num->rn_id[num->rn_len - 1] <= 1)
334b12ab1d2Sniallo 		return (num);
3352c56d68bSjoris 	num->rn_id[num->rn_len - 1]--;
3362c56d68bSjoris 	return (num);
3372c56d68bSjoris }
3382c56d68bSjoris 
3392c56d68bSjoris /*
3402043120eSjfb  * rcsnum_revtobr()
3412043120eSjfb  *
3422043120eSjfb  * Retrieve the branch number associated with the revision number <num>.
3432043120eSjfb  * If <num> is a branch revision, the returned value will be the same
3442043120eSjfb  * number as the argument.
3452043120eSjfb  */
3462043120eSjfb RCSNUM *
rcsnum_revtobr(const RCSNUM * num)3472043120eSjfb rcsnum_revtobr(const RCSNUM *num)
3482043120eSjfb {
3492043120eSjfb 	RCSNUM *brnum;
3502043120eSjfb 
3512043120eSjfb 	if (num->rn_len < 2)
3522043120eSjfb 		return (NULL);
3532043120eSjfb 
3543422a189Sjoris 	brnum = rcsnum_alloc();
3552043120eSjfb 	rcsnum_cpy(num, brnum, 0);
3562043120eSjfb 
3572043120eSjfb 	if (!RCSNUM_ISBRANCH(brnum))
3582043120eSjfb 		brnum->rn_len--;
3592043120eSjfb 
3602043120eSjfb 	return (brnum);
3612043120eSjfb }
3622043120eSjfb 
3632043120eSjfb /*
3642043120eSjfb  * rcsnum_brtorev()
3652043120eSjfb  *
3662043120eSjfb  * Retrieve the initial revision number associated with the branch number <num>.
3672043120eSjfb  * If <num> is a revision number, an error will be returned.
3682043120eSjfb  */
3692043120eSjfb RCSNUM *
rcsnum_brtorev(const RCSNUM * brnum)3702043120eSjfb rcsnum_brtorev(const RCSNUM *brnum)
3712043120eSjfb {
3722043120eSjfb 	RCSNUM *num;
3732043120eSjfb 
3742043120eSjfb 	if (!RCSNUM_ISBRANCH(brnum)) {
3752043120eSjfb 		return (NULL);
3762043120eSjfb 	}
3772043120eSjfb 
3783422a189Sjoris 	num = rcsnum_alloc();
37946fd924fSxsa 	rcsnum_setsize(num, brnum->rn_len + 1);
3802043120eSjfb 	rcsnum_cpy(brnum, num, brnum->rn_len);
3812043120eSjfb 	num->rn_id[num->rn_len++] = 1;
3822043120eSjfb 
3832043120eSjfb 	return (num);
3842043120eSjfb }
3852043120eSjfb 
38618cf7829Sjoris RCSNUM *
rcsnum_new_branch(RCSNUM * rev)387e8f8196dStobias rcsnum_new_branch(RCSNUM *rev)
388e8f8196dStobias {
389e8f8196dStobias 	RCSNUM *branch;
390e8f8196dStobias 
391e8f8196dStobias 	if (rev->rn_len > RCSNUM_MAXLEN - 1)
392e8f8196dStobias 		return NULL;
393e8f8196dStobias 
394e8f8196dStobias 	branch = rcsnum_alloc();
395e8f8196dStobias 	rcsnum_cpy(rev, branch, 0);
396e8f8196dStobias 	rcsnum_setsize(branch, rev->rn_len + 1);
397e8f8196dStobias 	branch->rn_id[branch->rn_len - 1] = 2;
398e8f8196dStobias 
399e8f8196dStobias 	return branch;
400e8f8196dStobias }
401e8f8196dStobias 
402e8f8196dStobias RCSNUM *
rcsnum_branch_root(RCSNUM * brev)40318cf7829Sjoris rcsnum_branch_root(RCSNUM *brev)
40418cf7829Sjoris {
40518cf7829Sjoris 	RCSNUM *root;
40618cf7829Sjoris 
40718cf7829Sjoris 	if (!RCSNUM_ISBRANCHREV(brev))
40818cf7829Sjoris 		fatal("rcsnum_branch_root: no revision on branch specified");
40918cf7829Sjoris 
41018cf7829Sjoris 	root = rcsnum_alloc();
41118cf7829Sjoris 	rcsnum_cpy(brev, root, 0);
41218cf7829Sjoris 	root->rn_len -= 2;
41318cf7829Sjoris 	return (root);
41418cf7829Sjoris }
41518cf7829Sjoris 
41646fd924fSxsa static void
rcsnum_setsize(RCSNUM * num,u_int len)4172043120eSjfb rcsnum_setsize(RCSNUM *num, u_int len)
4182043120eSjfb {
4192043120eSjfb 	num->rn_len = len;
4202043120eSjfb }
421883d6377Sjoris 
422883d6377Sjoris int
rcsnum_differ(RCSNUM * r1,RCSNUM * r2)423883d6377Sjoris rcsnum_differ(RCSNUM *r1, RCSNUM *r2)
424883d6377Sjoris {
425883d6377Sjoris 	int i, len;
426883d6377Sjoris 
427883d6377Sjoris 	if (r1->rn_len != r2->rn_len)
428883d6377Sjoris 		return (1);
429883d6377Sjoris 
430b9fc9a72Sderaadt 	len = MINIMUM(r1->rn_len, r2->rn_len);
431883d6377Sjoris 	for (i = 0; i < len; i++) {
432883d6377Sjoris 		if (r1->rn_id[i] != r2->rn_id[i])
433883d6377Sjoris 			return (1);
434883d6377Sjoris 	}
435883d6377Sjoris 
436883d6377Sjoris 	return (0);
437883d6377Sjoris }
438