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