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