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