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