xref: /openbsd/usr.bin/cvs/rcsnum.c (revision 3d8817e4)
1 /*	$OpenBSD: rcsnum.c,v 1.53 2008/05/22 07:03:02 joris Exp $	*/
2 /*
3  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <ctype.h>
28 #include <string.h>
29 
30 #include "cvs.h"
31 
32 static void	 rcsnum_setsize(RCSNUM *, u_int);
33 static char	*rcsnum_itoa(u_int16_t, char *, size_t);
34 
35 /*
36  * rcsnum_alloc()
37  *
38  * Allocate an RCS number structure and return a pointer to it.
39  */
40 RCSNUM *
41 rcsnum_alloc(void)
42 {
43 	RCSNUM *rnp;
44 
45 	rnp = xmalloc(sizeof(*rnp));
46 	rnp->rn_len = 0;
47 	rnp->rn_id = NULL;
48 
49 	return (rnp);
50 }
51 
52 /*
53  * rcsnum_addmagic()
54  *
55  * Adds a magic branch number to an RCS number.
56  * Returns 0 on success, or -1 on failure.
57  */
58 int
59 rcsnum_addmagic(RCSNUM *rn)
60 {
61 	if (!rn->rn_len || rn->rn_len > RCSNUM_MAXLEN - 1)
62 		return -1;
63 	rcsnum_setsize(rn, rn->rn_len + 1);
64 	rn->rn_id[rn->rn_len - 1] = rn->rn_id[rn->rn_len - 2];
65 	rn->rn_id[rn->rn_len - 2] = 0;
66 
67 	return 0;
68 }
69 
70 /*
71  * rcsnum_parse()
72  *
73  * Parse a string specifying an RCS number and return the corresponding RCSNUM.
74  */
75 RCSNUM *
76 rcsnum_parse(const char *str)
77 {
78 	char *ep;
79 	RCSNUM *num;
80 
81 	num = rcsnum_alloc();
82 	if (rcsnum_aton(str, &ep, num) < 0 || *ep != '\0') {
83 		rcsnum_free(num);
84 		num = NULL;
85 	}
86 
87 	return (num);
88 }
89 
90 /*
91  * rcsnum_free()
92  *
93  * Free an RCSNUM structure previously allocated with rcsnum_alloc().
94  */
95 void
96 rcsnum_free(RCSNUM *rn)
97 {
98 	if (rn->rn_id != NULL)
99 		xfree(rn->rn_id);
100 	xfree(rn);
101 }
102 
103 /*
104  * rcsnum_tostr()
105  *
106  * Format the RCS number <nump> into a human-readable dot-separated
107  * representation and store the resulting string in <buf>, which is of size
108  * <blen>.
109  * Returns a pointer to the start of <buf>.  On failure <buf> is set to
110  * an empty string.
111  */
112 char *
113 rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen)
114 {
115 	u_int i;
116 	char tmp[8];
117 
118 	if (nump == NULL || nump->rn_len == 0) {
119 		buf[0] = '\0';
120 		return (buf);
121 	}
122 
123 	if (strlcpy(buf, rcsnum_itoa(nump->rn_id[0], buf, blen), blen) >= blen)
124 		fatal("rcsnum_tostr: truncation");
125 	for (i = 1; i < nump->rn_len; i++) {
126 		const char *str;
127 
128 		str = rcsnum_itoa(nump->rn_id[i], tmp, sizeof(tmp));
129 		if (strlcat(buf, ".", blen) >= blen ||
130 		    strlcat(buf, str, blen) >= blen)
131 			fatal("rcsnum_tostr: truncation");
132 	}
133 
134 	return (buf);
135 }
136 
137 static char *
138 rcsnum_itoa(u_int16_t num, char *buf, size_t len)
139 {
140 	u_int16_t i;
141 	char *p;
142 
143 	if (num == 0)
144 		return "0";
145 
146 	p = buf + len - 1;
147 	i = num;
148 	bzero(buf, len);
149 	while (i) {
150 		*--p = '0' + (i % 10);
151 		i  /= 10;
152 	}
153 	return (p);
154 }
155 
156 /*
157  * rcsnum_cpy()
158  *
159  * Copy the number stored in <nsrc> in the destination <ndst> up to <depth>
160  * numbers deep.  If <depth> is 0, there is no depth limit.
161  */
162 void
163 rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth)
164 {
165 	u_int len;
166 
167 	len = nsrc->rn_len;
168 	if (depth != 0 && len > depth)
169 		len = depth;
170 
171 	rcsnum_setsize(ndst, len);
172 	/* Overflow checked in rcsnum_setsize(). */
173 	(void)memcpy(ndst->rn_id, nsrc->rn_id,
174 	    len * sizeof(*(nsrc->rn_id)));
175 }
176 
177 /*
178  * rcsnum_cmp()
179  *
180  * Compare the two numbers <n1> and <n2>. Returns -1 if <n1> is larger than
181  * <n2>, 0 if they are both the same, and 1 if <n2> is larger than <n1>.
182  * The <depth> argument specifies how many numbers deep should be checked for
183  * the result.  A value of 0 means that the depth will be the maximum of the
184  * two numbers, so that a longer number is considered greater than a shorter
185  * number if they are equal up to the minimum length.
186  */
187 int
188 rcsnum_cmp(RCSNUM *n1, RCSNUM *n2, u_int depth)
189 {
190 	int res;
191 	u_int i;
192 	size_t slen;
193 
194 	if (!rcsnum_differ(n1, n2))
195 		return (0);
196 
197 	slen = MIN(n1->rn_len, n2->rn_len);
198 	if (depth != 0 && slen > depth)
199 		slen = depth;
200 
201 	for (i = 0; i < slen; i++) {
202 		res = n1->rn_id[i] - n2->rn_id[i];
203 		if (res < 0)
204 			return (1);
205 		else if (res > 0)
206 			return (-1);
207 	}
208 
209 	/* If an explicit depth was specified, and we've
210 	 * already checked up to depth, consider the
211 	 * revision numbers equal. */
212 	if (depth != 0 && slen == depth)
213 		return (0);
214 	else if (n1->rn_len > n2->rn_len)
215 		return (-1);
216 	else if (n2->rn_len > n1->rn_len)
217 		return (1);
218 
219 	return (0);
220 }
221 
222 /*
223  * rcsnum_aton()
224  *
225  * Translate the string <str> containing a sequence of digits and periods into
226  * its binary representation, which is stored in <nump>.  The address of the
227  * first byte not part of the number is stored in <ep> on return, if it is not
228  * NULL.
229  * Returns 0 on success, or -1 on failure.
230  */
231 int
232 rcsnum_aton(const char *str, char **ep, RCSNUM *nump)
233 {
234 	u_int32_t val;
235 	const char *sp;
236 	char *s;
237 
238 	if (nump->rn_id == NULL)
239 		nump->rn_id = xmalloc(sizeof(*(nump->rn_id)));
240 
241 	nump->rn_len = 0;
242 	nump->rn_id[0] = 0;
243 
244 	for (sp = str;; sp++) {
245 		if (!isdigit(*sp) && (*sp != '.'))
246 			break;
247 
248 		if (*sp == '.') {
249 			if (nump->rn_len >= RCSNUM_MAXLEN - 1)
250 				goto rcsnum_aton_failed;
251 
252 			nump->rn_len++;
253 			nump->rn_id = xrealloc(nump->rn_id,
254 			    nump->rn_len + 1, sizeof(*(nump->rn_id)));
255 			nump->rn_id[nump->rn_len] = 0;
256 			continue;
257 		}
258 
259 		val = (nump->rn_id[nump->rn_len] * 10) + (*sp - '0');
260 		if (val > RCSNUM_MAXNUM)
261 			fatal("RCSNUM overflow!");
262 
263 		nump->rn_id[nump->rn_len] = val;
264 	}
265 
266 	if (ep != NULL)
267 		*(const char **)ep = sp;
268 
269 	/*
270 	 * Handle "magic" RCS branch numbers.
271 	 *
272 	 * What are they?
273 	 *
274 	 * Magic branch numbers have an extra .0. at the second farmost
275 	 * rightside of the branch number, so instead of having an odd
276 	 * number of dot-separated decimals, it will have an even number.
277 	 *
278 	 * Now, according to all the documentation I've found on the net
279 	 * about this, cvs does this for "efficiency reasons", I'd like
280 	 * to hear one.
281 	 *
282 	 * We just make sure we remove the .0. from in the branch number.
283 	 *
284 	 * XXX - for compatibility reasons with GNU cvs we _need_
285 	 * to skip this part for the 'log' command, apparently it does
286 	 * show the magic branches for an unknown and probably
287 	 * completely insane and not understandable reason in that output.
288 	 *
289 	 */
290 	if (nump->rn_len > 2 && nump->rn_id[nump->rn_len - 1] == 0) {
291 		/*
292 		 * Look for ".0.x" at the end of the branch number.
293 		 */
294 		if ((s = strrchr(str, '.')) != NULL) {
295 			s--;
296 			while (*s != '.')
297 				s--;
298 
299 			/*
300 			 * If we have a "magic" branch, adjust it
301 			 * so the .0. is removed.
302 			 */
303 			if (!strncmp(s, RCS_MAGIC_BRANCH,
304 			    sizeof(RCS_MAGIC_BRANCH) - 1)) {
305 				nump->rn_id[nump->rn_len - 1] =
306 				    nump->rn_id[nump->rn_len];
307 				nump->rn_len--;
308 			}
309 		}
310 	}
311 
312 	/* We can't have a single-digit rcs number. */
313 	if (nump->rn_len == 0) {
314 		nump->rn_len++;
315 		nump->rn_id = xrealloc(nump->rn_id,
316 		    nump->rn_len + 1, sizeof(*(nump->rn_id)));
317 		nump->rn_id[nump->rn_len] = 0;
318 	}
319 
320 	nump->rn_len++;
321 	return (nump->rn_len);
322 
323 rcsnum_aton_failed:
324 	nump->rn_len = 0;
325 	xfree(nump->rn_id);
326 	nump->rn_id = NULL;
327 	return (-1);
328 }
329 
330 /*
331  * rcsnum_inc()
332  *
333  * Increment the revision number specified in <num>.
334  * Returns a pointer to the <num> on success, or NULL on failure.
335  */
336 RCSNUM *
337 rcsnum_inc(RCSNUM *num)
338 {
339 	if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM)
340 		return (NULL);
341 	num->rn_id[num->rn_len - 1]++;
342 	return (num);
343 }
344 
345 /*
346  * rcsnum_dec()
347  *
348  * Decreases the revision number specified in <num>, if doing so will not
349  * result in an ending value below 1. E.g. 4.2 will go to 4.1 but 4.1 will
350  * be returned as 4.1.
351  */
352 RCSNUM *
353 rcsnum_dec(RCSNUM *num)
354 {
355 	/* XXX - Is it an error for the number to be 0? */
356 	if (num->rn_id[num->rn_len - 1] <= 1)
357 		return (num);
358 	num->rn_id[num->rn_len - 1]--;
359 	return (num);
360 }
361 
362 /*
363  * rcsnum_revtobr()
364  *
365  * Retrieve the branch number associated with the revision number <num>.
366  * If <num> is a branch revision, the returned value will be the same
367  * number as the argument.
368  */
369 RCSNUM *
370 rcsnum_revtobr(const RCSNUM *num)
371 {
372 	RCSNUM *brnum;
373 
374 	if (num->rn_len < 2)
375 		return (NULL);
376 
377 	brnum = rcsnum_alloc();
378 	rcsnum_cpy(num, brnum, 0);
379 
380 	if (!RCSNUM_ISBRANCH(brnum))
381 		brnum->rn_len--;
382 
383 	return (brnum);
384 }
385 
386 /*
387  * rcsnum_brtorev()
388  *
389  * Retrieve the initial revision number associated with the branch number <num>.
390  * If <num> is a revision number, an error will be returned.
391  */
392 RCSNUM *
393 rcsnum_brtorev(const RCSNUM *brnum)
394 {
395 	RCSNUM *num;
396 
397 	if (!RCSNUM_ISBRANCH(brnum)) {
398 		return (NULL);
399 	}
400 
401 	num = rcsnum_alloc();
402 	rcsnum_setsize(num, brnum->rn_len + 1);
403 	rcsnum_cpy(brnum, num, brnum->rn_len);
404 	num->rn_id[num->rn_len++] = 1;
405 
406 	return (num);
407 }
408 
409 RCSNUM *
410 rcsnum_new_branch(RCSNUM *rev)
411 {
412 	RCSNUM *branch;
413 
414 	if (rev->rn_len > RCSNUM_MAXLEN - 1)
415 		return NULL;
416 
417 	branch = rcsnum_alloc();
418 	rcsnum_cpy(rev, branch, 0);
419 	rcsnum_setsize(branch, rev->rn_len + 1);
420 	branch->rn_id[branch->rn_len - 1] = 2;
421 
422 	return branch;
423 }
424 
425 RCSNUM *
426 rcsnum_branch_root(RCSNUM *brev)
427 {
428 	RCSNUM *root;
429 
430 	if (!RCSNUM_ISBRANCHREV(brev))
431 		fatal("rcsnum_branch_root: no revision on branch specified");
432 
433 	root = rcsnum_alloc();
434 	rcsnum_cpy(brev, root, 0);
435 	root->rn_len -= 2;
436 	return (root);
437 }
438 
439 static void
440 rcsnum_setsize(RCSNUM *num, u_int len)
441 {
442 	num->rn_id = xrealloc(num->rn_id, len, sizeof(*(num->rn_id)));
443 	num->rn_len = len;
444 }
445 
446 int
447 rcsnum_differ(RCSNUM *r1, RCSNUM *r2)
448 {
449 	int i, len;
450 
451 	if (r1->rn_len != r2->rn_len)
452 		return (1);
453 
454 	len = MIN(r1->rn_len, r2->rn_len);
455 	for (i = 0; i < len; i++) {
456 		if (r1->rn_id[i] != r2->rn_id[i])
457 			return (1);
458 	}
459 
460 	return (0);
461 }
462