xref: /openbsd/usr.bin/cvs/rcsnum.c (revision 8529ddd3)
1 /*	$OpenBSD: rcsnum.c,v 1.56 2015/01/16 06:40:07 deraadt 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 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
33 
34 static void	 rcsnum_setsize(RCSNUM *, u_int);
35 static char	*rcsnum_itoa(u_int16_t, char *, size_t);
36 
37 /*
38  * rcsnum_alloc()
39  *
40  * Allocate an RCS number structure and return a pointer to it.
41  */
42 RCSNUM *
43 rcsnum_alloc(void)
44 {
45 	RCSNUM *rnp;
46 
47 	rnp = xcalloc(1, sizeof(*rnp));
48 	rnp->rn_len = 0;
49 
50 	return (rnp);
51 }
52 
53 /*
54  * rcsnum_addmagic()
55  *
56  * Adds a magic branch number to an RCS number.
57  * Returns 0 on success, or -1 on failure.
58  */
59 int
60 rcsnum_addmagic(RCSNUM *rn)
61 {
62 	if (!rn->rn_len || rn->rn_len > RCSNUM_MAXLEN - 1)
63 		return -1;
64 	rcsnum_setsize(rn, rn->rn_len + 1);
65 	rn->rn_id[rn->rn_len - 1] = rn->rn_id[rn->rn_len - 2];
66 	rn->rn_id[rn->rn_len - 2] = 0;
67 
68 	return 0;
69 }
70 
71 /*
72  * rcsnum_parse()
73  *
74  * Parse a string specifying an RCS number and return the corresponding RCSNUM.
75  */
76 RCSNUM *
77 rcsnum_parse(const char *str)
78 {
79 	char *ep;
80 	RCSNUM *num;
81 
82 	num = rcsnum_alloc();
83 	if (rcsnum_aton(str, &ep, num) < 0 || *ep != '\0') {
84 		rcsnum_free(num);
85 		num = NULL;
86 	}
87 
88 	return (num);
89 }
90 
91 /*
92  * rcsnum_free()
93  *
94  * Free an RCSNUM structure previously allocated with rcsnum_alloc().
95  */
96 void
97 rcsnum_free(RCSNUM *rn)
98 {
99 	xfree(rn);
100 }
101 
102 /*
103  * rcsnum_tostr()
104  *
105  * Format the RCS number <nump> into a human-readable dot-separated
106  * representation and store the resulting string in <buf>, which is of size
107  * <blen>.
108  * Returns a pointer to the start of <buf>.  On failure <buf> is set to
109  * an empty string.
110  */
111 char *
112 rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen)
113 {
114 	u_int i;
115 	char tmp[8];
116 
117 	if (nump == NULL || nump->rn_len == 0) {
118 		buf[0] = '\0';
119 		return (buf);
120 	}
121 
122 	if (strlcpy(buf, rcsnum_itoa(nump->rn_id[0], buf, blen), blen) >= blen)
123 		fatal("rcsnum_tostr: truncation");
124 	for (i = 1; i < nump->rn_len; i++) {
125 		const char *str;
126 
127 		str = rcsnum_itoa(nump->rn_id[i], tmp, sizeof(tmp));
128 		if (strlcat(buf, ".", blen) >= blen ||
129 		    strlcat(buf, str, blen) >= blen)
130 			fatal("rcsnum_tostr: truncation");
131 	}
132 	return (buf);
133 }
134 
135 static char *
136 rcsnum_itoa(u_int16_t num, char *buf, size_t len)
137 {
138 	u_int16_t i;
139 	char *p;
140 
141 	if (num == 0)
142 		return "0";
143 
144 	p = buf + len - 1;
145 	i = num;
146 	bzero(buf, len);
147 	while (i) {
148 		*--p = '0' + (i % 10);
149 		i  /= 10;
150 	}
151 	return (p);
152 }
153 
154 /*
155  * rcsnum_cpy()
156  *
157  * Copy the number stored in <nsrc> in the destination <ndst> up to <depth>
158  * numbers deep.  If <depth> is 0, there is no depth limit.
159  */
160 void
161 rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth)
162 {
163 	u_int len;
164 
165 	len = nsrc->rn_len;
166 	if (depth != 0 && len > depth)
167 		len = depth;
168 
169 	rcsnum_setsize(ndst, len);
170 	memcpy(ndst->rn_id, nsrc->rn_id, len * sizeof(*(nsrc->rn_id)));
171 }
172 
173 /*
174  * rcsnum_cmp()
175  *
176  * Compare the two numbers <n1> and <n2>. Returns -1 if <n1> is larger than
177  * <n2>, 0 if they are both the same, and 1 if <n2> is larger than <n1>.
178  * The <depth> argument specifies how many numbers deep should be checked for
179  * the result.  A value of 0 means that the depth will be the maximum of the
180  * two numbers, so that a longer number is considered greater than a shorter
181  * number if they are equal up to the minimum length.
182  */
183 int
184 rcsnum_cmp(RCSNUM *n1, RCSNUM *n2, u_int depth)
185 {
186 	int res;
187 	u_int i;
188 	size_t slen;
189 
190 	if (!rcsnum_differ(n1, n2))
191 		return (0);
192 
193 	slen = MINIMUM(n1->rn_len, n2->rn_len);
194 	if (depth != 0 && slen > depth)
195 		slen = depth;
196 
197 	for (i = 0; i < slen; i++) {
198 		res = n1->rn_id[i] - n2->rn_id[i];
199 		if (res < 0)
200 			return (1);
201 		else if (res > 0)
202 			return (-1);
203 	}
204 
205 	/* If an explicit depth was specified, and we've
206 	 * already checked up to depth, consider the
207 	 * revision numbers equal. */
208 	if (depth != 0 && slen == depth)
209 		return (0);
210 	else if (n1->rn_len > n2->rn_len)
211 		return (-1);
212 	else if (n2->rn_len > n1->rn_len)
213 		return (1);
214 
215 	return (0);
216 }
217 
218 /*
219  * rcsnum_aton()
220  *
221  * Translate the string <str> containing a sequence of digits and periods into
222  * its binary representation, which is stored in <nump>.  The address of the
223  * first byte not part of the number is stored in <ep> on return, if it is not
224  * NULL.
225  * Returns 0 on success, or -1 on failure.
226  */
227 int
228 rcsnum_aton(const char *str, char **ep, RCSNUM *nump)
229 {
230 	u_int32_t val;
231 	const char *sp;
232 	char *s;
233 
234 	nump->rn_len = 0;
235 	nump->rn_id[0] = 0;
236 
237 	for (sp = str;; sp++) {
238 		if (!isdigit((unsigned char)*sp) && (*sp != '.'))
239 			break;
240 
241 		if (*sp == '.') {
242 			if (nump->rn_len >= RCSNUM_MAXLEN - 1)
243 				goto rcsnum_aton_failed;
244 
245 			nump->rn_len++;
246 			nump->rn_id[nump->rn_len] = 0;
247 			continue;
248 		}
249 
250 		val = (nump->rn_id[nump->rn_len] * 10) + (*sp - '0');
251 		if (val > RCSNUM_MAXNUM)
252 			fatal("RCSNUM overflow!");
253 
254 		nump->rn_id[nump->rn_len] = val;
255 	}
256 
257 	if (ep != NULL)
258 		*(const char **)ep = sp;
259 
260 	/*
261 	 * Handle "magic" RCS branch numbers.
262 	 *
263 	 * What are they?
264 	 *
265 	 * Magic branch numbers have an extra .0. at the second farmost
266 	 * rightside of the branch number, so instead of having an odd
267 	 * number of dot-separated decimals, it will have an even number.
268 	 *
269 	 * Now, according to all the documentation I've found on the net
270 	 * about this, cvs does this for "efficiency reasons", I'd like
271 	 * to hear one.
272 	 *
273 	 * We just make sure we remove the .0. from in the branch number.
274 	 *
275 	 * XXX - for compatibility reasons with GNU cvs we _need_
276 	 * to skip this part for the 'log' command, apparently it does
277 	 * show the magic branches for an unknown and probably
278 	 * completely insane and not understandable reason in that output.
279 	 *
280 	 */
281 	if (nump->rn_len > 2 && nump->rn_id[nump->rn_len - 1] == 0) {
282 		/*
283 		 * Look for ".0.x" at the end of the branch number.
284 		 */
285 		if ((s = strrchr(str, '.')) != NULL) {
286 			s--;
287 			while (*s != '.')
288 				s--;
289 
290 			/*
291 			 * If we have a "magic" branch, adjust it
292 			 * so the .0. is removed.
293 			 */
294 			if (!strncmp(s, RCS_MAGIC_BRANCH,
295 			    sizeof(RCS_MAGIC_BRANCH) - 1)) {
296 				nump->rn_id[nump->rn_len - 1] =
297 				    nump->rn_id[nump->rn_len];
298 				nump->rn_len--;
299 			}
300 		}
301 	}
302 
303 	/* We can't have a single-digit rcs number. */
304 	if (nump->rn_len == 0) {
305 		nump->rn_len++;
306 		nump->rn_id[nump->rn_len] = 0;
307 	}
308 
309 	nump->rn_len++;
310 	return (nump->rn_len);
311 
312 rcsnum_aton_failed:
313 	nump->rn_len = 0;
314 	return (-1);
315 }
316 
317 /*
318  * rcsnum_inc()
319  *
320  * Increment the revision number specified in <num>.
321  * Returns a pointer to the <num> on success, or NULL on failure.
322  */
323 RCSNUM *
324 rcsnum_inc(RCSNUM *num)
325 {
326 	if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM)
327 		return (NULL);
328 	num->rn_id[num->rn_len - 1]++;
329 	return (num);
330 }
331 
332 /*
333  * rcsnum_dec()
334  *
335  * Decreases the revision number specified in <num>, if doing so will not
336  * result in an ending value below 1. E.g. 4.2 will go to 4.1 but 4.1 will
337  * be returned as 4.1.
338  */
339 RCSNUM *
340 rcsnum_dec(RCSNUM *num)
341 {
342 	/* XXX - Is it an error for the number to be 0? */
343 	if (num->rn_id[num->rn_len - 1] <= 1)
344 		return (num);
345 	num->rn_id[num->rn_len - 1]--;
346 	return (num);
347 }
348 
349 /*
350  * rcsnum_revtobr()
351  *
352  * Retrieve the branch number associated with the revision number <num>.
353  * If <num> is a branch revision, the returned value will be the same
354  * number as the argument.
355  */
356 RCSNUM *
357 rcsnum_revtobr(const RCSNUM *num)
358 {
359 	RCSNUM *brnum;
360 
361 	if (num->rn_len < 2)
362 		return (NULL);
363 
364 	brnum = rcsnum_alloc();
365 	rcsnum_cpy(num, brnum, 0);
366 
367 	if (!RCSNUM_ISBRANCH(brnum))
368 		brnum->rn_len--;
369 
370 	return (brnum);
371 }
372 
373 /*
374  * rcsnum_brtorev()
375  *
376  * Retrieve the initial revision number associated with the branch number <num>.
377  * If <num> is a revision number, an error will be returned.
378  */
379 RCSNUM *
380 rcsnum_brtorev(const RCSNUM *brnum)
381 {
382 	RCSNUM *num;
383 
384 	if (!RCSNUM_ISBRANCH(brnum)) {
385 		return (NULL);
386 	}
387 
388 	num = rcsnum_alloc();
389 	rcsnum_setsize(num, brnum->rn_len + 1);
390 	rcsnum_cpy(brnum, num, brnum->rn_len);
391 	num->rn_id[num->rn_len++] = 1;
392 
393 	return (num);
394 }
395 
396 RCSNUM *
397 rcsnum_new_branch(RCSNUM *rev)
398 {
399 	RCSNUM *branch;
400 
401 	if (rev->rn_len > RCSNUM_MAXLEN - 1)
402 		return NULL;
403 
404 	branch = rcsnum_alloc();
405 	rcsnum_cpy(rev, branch, 0);
406 	rcsnum_setsize(branch, rev->rn_len + 1);
407 	branch->rn_id[branch->rn_len - 1] = 2;
408 
409 	return branch;
410 }
411 
412 RCSNUM *
413 rcsnum_branch_root(RCSNUM *brev)
414 {
415 	RCSNUM *root;
416 
417 	if (!RCSNUM_ISBRANCHREV(brev))
418 		fatal("rcsnum_branch_root: no revision on branch specified");
419 
420 	root = rcsnum_alloc();
421 	rcsnum_cpy(brev, root, 0);
422 	root->rn_len -= 2;
423 	return (root);
424 }
425 
426 static void
427 rcsnum_setsize(RCSNUM *num, u_int len)
428 {
429 	num->rn_len = len;
430 }
431 
432 int
433 rcsnum_differ(RCSNUM *r1, RCSNUM *r2)
434 {
435 	int i, len;
436 
437 	if (r1->rn_len != r2->rn_len)
438 		return (1);
439 
440 	len = MINIMUM(r1->rn_len, r2->rn_len);
441 	for (i = 0; i < len; i++) {
442 		if (r1->rn_id[i] != r2->rn_id[i])
443 			return (1);
444 	}
445 
446 	return (0);
447 }
448