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