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