rcsnum.c revision 1.19
1/*	$OpenBSD: rcsnum.c,v 1.19 2005/12/12 17:47:03 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 <sys/param.h>
28
29#include <ctype.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33
34#include "cvs.h"
35#include "log.h"
36#include "rcs.h"
37
38
39static int	rcsnum_setsize(RCSNUM *, u_int);
40
41
42/*
43 * rcsnum_alloc()
44 *
45 * Allocate an RCS number structure and return a pointer to it on success,
46 * or NULL on failure.
47 */
48RCSNUM *
49rcsnum_alloc(void)
50{
51	RCSNUM *rnp;
52
53	rnp = (RCSNUM *)xmalloc(sizeof(*rnp));
54	rnp->rn_len = 0;
55	rnp->rn_id = NULL;
56
57	return (rnp);
58}
59
60/*
61 * rcsnum_parse()
62 *
63 * Parse a string specifying an RCS number and return the corresponding RCSNUM.
64 */
65RCSNUM *
66rcsnum_parse(const char *str)
67{
68	char *ep;
69	RCSNUM *num;
70
71	num = rcsnum_alloc();
72	if ((rcsnum_aton(str, &ep, num) < 0) || (*ep != '\0')) {
73		rcsnum_free(num);
74		num = NULL;
75		if (*ep != '\0')
76			rcs_errno = RCS_ERR_BADNUM;
77	}
78
79	return (num);
80}
81
82/*
83 * rcsnum_free()
84 *
85 * Free an RCSNUM structure previously allocated with rcsnum_alloc().
86 */
87void
88rcsnum_free(RCSNUM *rn)
89{
90	if (rn->rn_id != NULL)
91		xfree(rn->rn_id);
92	xfree(rn);
93}
94
95/*
96 * rcsnum_tostr()
97 *
98 * Format the RCS number <nump> into a human-readable dot-separated
99 * representation and store the resulting string in <buf>, which is of size
100 * <blen>.
101 * Returns a pointer to the start of <buf> on success, or NULL on failure.
102 */
103char *
104rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen)
105{
106	u_int i;
107	char tmp[8];
108
109	if ((nump == NULL) || (nump->rn_len == 0)) {
110		buf[0] = '\0';
111		return (buf);
112	}
113
114	snprintf(buf, blen, "%u", nump->rn_id[0]);
115	for (i = 1; i < nump->rn_len; i++) {
116		snprintf(tmp, sizeof(tmp), ".%u", nump->rn_id[i]);
117		strlcat(buf, tmp, blen);
118	}
119
120	return (buf);
121}
122
123/*
124 * rcsnum_cpy()
125 *
126 * Copy the number stored in <nsrc> in the destination <ndst> up to <depth>
127 * numbers deep.
128 * Returns 0 on success, or -1 on failure.
129 */
130int
131rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth)
132{
133	u_int len;
134	size_t sz;
135	void *tmp;
136
137	len = nsrc->rn_len;
138	if ((depth != 0) && (len > depth))
139		len = depth;
140	sz = len * sizeof(u_int16_t);
141
142	tmp = xrealloc(ndst->rn_id, sz);
143	ndst->rn_id = (u_int16_t *)tmp;
144	ndst->rn_len = len;
145	memcpy(ndst->rn_id, nsrc->rn_id, sz);
146	return (0);
147}
148
149/*
150 * rcsnum_cmp()
151 *
152 * Compare the two numbers <n1> and <n2>. Returns -1 if <n1> is larger than
153 * <n2>, 0 if they are both the same, and 1 if <n2> is larger than <n1>.
154 * The <depth> argument specifies how many numbers deep should be checked for
155 * the result.  A value of 0 means that the depth will be the minimum of the
156 * two numbers.
157 */
158int
159rcsnum_cmp(const RCSNUM *n1, const RCSNUM *n2, u_int depth)
160{
161	int res;
162	u_int i;
163	size_t slen;
164
165	slen = MIN(n1->rn_len, n2->rn_len);
166	if ((depth != 0) && (slen > depth))
167		slen = depth;
168
169	for (i = 0; i < slen; i++) {
170		res = n1->rn_id[i] - n2->rn_id[i];
171		if (res < 0)
172			return (1);
173		else if (res > 0)
174			return (-1);
175	}
176
177	if (n1->rn_len > n2->rn_len)
178		return (-1);
179	else if (n2->rn_len > n1->rn_len)
180		return (1);
181
182	return (0);
183}
184
185/*
186 * rcsnum_aton()
187 *
188 * Translate the string <str> containing a sequence of digits and periods into
189 * its binary representation, which is stored in <nump>.  The address of the
190 * first byte not part of the number is stored in <ep> on return, if it is not
191 * NULL.
192 * Returns 0 on success, or -1 on failure.
193 */
194int
195rcsnum_aton(const char *str, char **ep, RCSNUM *nump)
196{
197	u_int32_t val;
198	const char *sp;
199	void *tmp;
200	char *s;
201
202	if (nump->rn_id == NULL)
203		nump->rn_id = (u_int16_t *)xmalloc(sizeof(u_int16_t));
204
205	nump->rn_len = 0;
206	nump->rn_id[0] = 0;
207
208	for (sp = str;; sp++) {
209		if (!isdigit(*sp) && (*sp != '.'))
210			break;
211
212		if (*sp == '.') {
213			if (nump->rn_len >= RCSNUM_MAXLEN - 1) {
214				rcs_errno = RCS_ERR_BADNUM;
215				goto rcsnum_aton_failed;
216			}
217
218			nump->rn_len++;
219			tmp = xrealloc(nump->rn_id,
220			    (nump->rn_len + 1) * sizeof(u_int16_t));
221			nump->rn_id = (u_int16_t *)tmp;
222			nump->rn_id[nump->rn_len] = 0;
223			continue;
224		}
225
226		val = (nump->rn_id[nump->rn_len] * 10) + (*sp - 0x30);
227		if (val > RCSNUM_MAXNUM) {
228			cvs_log(LP_ERR, "RCSNUM overflow");
229			goto rcsnum_aton_failed;
230		}
231
232		nump->rn_id[nump->rn_len] = val;
233	}
234
235	if (ep != NULL)
236		*(const char **)ep = sp;
237
238	/*
239	 * Handle "magic" RCS branch numbers.
240	 *
241	 * What are they?
242	 *
243	 * Magic branch numbers have an extra .0. at the second farmost
244	 * rightside of the branch number, so instead of having an odd
245	 * number of dot-separated decimals, it will have an even number.
246	 *
247	 * Now, according to all the documentation i've found on the net
248	 * about this, cvs does this for "efficiency reasons", i'd like
249	 * to hear one.
250	 *
251	 * We just make sure we remove the .0. from in the branch number.
252	 *
253	 * XXX - for compatibility reasons with GNU cvs we _need_
254	 * to skip this part for the 'log' command, apparently it does
255	 * show the magic branches for an unknown and probably
256	 * completely insane and not understandable reason in that output.
257	 *
258	 */
259#if !defined(RCSPROG)
260	if ((nump->rn_len > 2) && (nump->rn_id[nump->rn_len - 1] == 0)
261	    && (cvs_cmdop != CVS_OP_LOG)) {
262#else
263	if ((nump->rn_len > 2) && (nump->rn_id[nump->rn_len - 1] == 0)) {
264#endif
265		/*
266		 * Look for ".0.x" at the end of the branch number.
267		 */
268		if ((s = strrchr(str, '.')) != NULL) {
269			*s--;
270			while (*s != '.')
271				*s--;
272
273			/*
274			 * If we have a "magic" branch, adjust it
275			 * so the .0. is removed.
276			 */
277			if (!strncmp(s, RCS_MAGIC_BRANCH,
278			    strlen(RCS_MAGIC_BRANCH))) {
279				nump->rn_id[nump->rn_len - 1] =
280				    nump->rn_id[nump->rn_len];
281				nump->rn_len--;
282			}
283		}
284	}
285
286	nump->rn_len++;
287	return (nump->rn_len);
288
289rcsnum_aton_failed:
290	nump->rn_len = 0;
291	xfree(nump->rn_id);
292	nump->rn_id = NULL;
293	return (-1);
294}
295
296/*
297 * rcsnum_inc()
298 *
299 * Increment the revision number specified in <num>.
300 * Returns a pointer to the <num> on success, or NULL on failure.
301 */
302RCSNUM *
303rcsnum_inc(RCSNUM *num)
304{
305	if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM)
306		return (NULL);
307	num->rn_id[num->rn_len - 1]++;
308	return (num);
309}
310
311/*
312 * rcsnum_dec()
313 *
314 * Decreases the revision number specified in <num>
315 * Returns pointer to the <num> on success, or NULL on failure.
316 */
317RCSNUM *
318rcsnum_dec(RCSNUM *num)
319{
320	if (num->rn_id[num->rn_len - 1] <= 0)
321		return (NULL);
322	num->rn_id[num->rn_len - 1]--;
323	return (num);
324}
325
326/*
327 * rcsnum_revtobr()
328 *
329 * Retrieve the branch number associated with the revision number <num>.
330 * If <num> is a branch revision, the returned value will be the same
331 * number as the argument.
332 */
333RCSNUM *
334rcsnum_revtobr(const RCSNUM *num)
335{
336	RCSNUM *brnum;
337
338	if (num->rn_len < 2)
339		return (NULL);
340
341	brnum = rcsnum_alloc();
342	rcsnum_cpy(num, brnum, 0);
343
344	if (!RCSNUM_ISBRANCH(brnum))
345		brnum->rn_len--;
346
347	return (brnum);
348}
349
350/*
351 * rcsnum_brtorev()
352 *
353 * Retrieve the initial revision number associated with the branch number <num>.
354 * If <num> is a revision number, an error will be returned.
355 */
356RCSNUM *
357rcsnum_brtorev(const RCSNUM *brnum)
358{
359	RCSNUM *num;
360
361	if (!RCSNUM_ISBRANCH(brnum)) {
362		return (NULL);
363	}
364
365	num = rcsnum_alloc();
366	if (rcsnum_setsize(num, brnum->rn_len + 1) < 0) {
367		rcsnum_free(num);
368		return (NULL);
369	}
370
371	rcsnum_cpy(brnum, num, brnum->rn_len);
372	num->rn_id[num->rn_len++] = 1;
373
374	return (num);
375}
376
377static int
378rcsnum_setsize(RCSNUM *num, u_int len)
379{
380	void *tmp;
381
382	tmp = xrealloc(num->rn_id, len * sizeof(u_int16_t));
383	num->rn_id = (u_int16_t *)tmp;
384	num->rn_len = len;
385	return (0);
386}
387