rcsnum.c revision 1.39
1/* $OpenBSD: rcsnum.c,v 1.39 2006/07/27 03:28:36 ray 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 33static void rcsnum_setsize(RCSNUM *, u_int); 34static char *rcsnum_itoa(u_int16_t, char *, size_t); 35 36int rcsnum_flags; 37 38/* 39 * rcsnum_alloc() 40 * 41 * Allocate an RCS number structure and return a pointer to it. 42 */ 43RCSNUM * 44rcsnum_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 */ 60RCSNUM * 61rcsnum_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 */ 82void 83rcsnum_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 */ 99char * 100rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen) 101{ 102 u_int i; 103 char tmp[8]; 104 size_t len; 105 106 if (nump == NULL || nump->rn_len == 0) { 107 buf[0] = '\0'; 108 return (buf); 109 } 110 111 strlcpy(buf, rcsnum_itoa(nump->rn_id[0], buf, blen), blen); 112 for (i = 1; i < nump->rn_len; i++) { 113 len = strlcat(buf, ".", blen); 114 if (len >= blen) 115 fatal("rcsnum_tostr: overflow 1"); 116 117 len = strlcat(buf, 118 rcsnum_itoa(nump->rn_id[i], tmp, sizeof(tmp)), blen); 119 if (len >= blen) 120 fatal("rcsnum_tostr: overflow 2"); 121 } 122 123 return (buf); 124} 125 126static char * 127rcsnum_itoa(u_int16_t num, char *buf, size_t len) 128{ 129 u_int16_t i; 130 char *p; 131 132 if (num == 0) 133 return "0"; 134 135 p = buf + len - 1; 136 i = num; 137 bzero(buf, len); 138 while (i) { 139 *--p = '0' + (i % 10); 140 i /= 10; 141 } 142 return (p); 143} 144 145/* 146 * rcsnum_cpy() 147 * 148 * Copy the number stored in <nsrc> in the destination <ndst> up to <depth> 149 * numbers deep. If <depth> is 0, there is no depth limit. 150 */ 151void 152rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth) 153{ 154 u_int len; 155 void *tmp; 156 157 len = nsrc->rn_len; 158 if (depth != 0 && len > depth) 159 len = depth; 160 161 tmp = xrealloc(ndst->rn_id, len, sizeof(*(nsrc->rn_id))); 162 ndst->rn_id = tmp; 163 ndst->rn_len = len; 164 /* Overflow checked in xrealloc(). */ 165 (void)memcpy(ndst->rn_id, nsrc->rn_id, 166 len * sizeof(*(nsrc->rn_id))); 167} 168 169/* 170 * rcsnum_cmp() 171 * 172 * Compare the two numbers <n1> and <n2>. Returns -1 if <n1> is larger than 173 * <n2>, 0 if they are both the same, and 1 if <n2> is larger than <n1>. 174 * The <depth> argument specifies how many numbers deep should be checked for 175 * the result. A value of 0 means that the depth will be the minimum of the 176 * two numbers. 177 */ 178int 179rcsnum_cmp(RCSNUM *n1, RCSNUM *n2, u_int depth) 180{ 181 int res; 182 u_int i; 183 size_t slen; 184 185 if (!rcsnum_differ(n1, n2)) 186 return (0); 187 188 slen = MIN(n1->rn_len, n2->rn_len); 189 if (depth != 0 && slen > depth) 190 slen = depth; 191 192 for (i = 0; i < slen; i++) { 193 res = n1->rn_id[i] - n2->rn_id[i]; 194 if (res < 0) 195 return (1); 196 else if (res > 0) 197 return (-1); 198 } 199 200 if (n1->rn_len > n2->rn_len) 201 return (-1); 202 else if (n2->rn_len > n1->rn_len) 203 return (1); 204 205 return (0); 206} 207 208/* 209 * rcsnum_aton() 210 * 211 * Translate the string <str> containing a sequence of digits and periods into 212 * its binary representation, which is stored in <nump>. The address of the 213 * first byte not part of the number is stored in <ep> on return, if it is not 214 * NULL. 215 * Returns 0 on success, or -1 on failure. 216 */ 217int 218rcsnum_aton(const char *str, char **ep, RCSNUM *nump) 219{ 220 u_int32_t val; 221 const char *sp; 222 void *tmp; 223 char *s; 224 225 if (nump->rn_id == NULL) 226 nump->rn_id = xmalloc(sizeof(*(nump->rn_id))); 227 228 nump->rn_len = 0; 229 nump->rn_id[0] = 0; 230 231 for (sp = str;; sp++) { 232 if (!isdigit(*sp) && (*sp != '.')) 233 break; 234 235 if (*sp == '.') { 236 if (nump->rn_len >= RCSNUM_MAXLEN - 1) { 237 rcs_errno = RCS_ERR_BADNUM; 238 goto rcsnum_aton_failed; 239 } 240 241 nump->rn_len++; 242 tmp = xrealloc(nump->rn_id, 243 nump->rn_len + 1, sizeof(*(nump->rn_id))); 244 nump->rn_id = tmp; 245 nump->rn_id[nump->rn_len] = 0; 246 continue; 247 } 248 249 val = (nump->rn_id[nump->rn_len] * 10) + (*sp - 0x30); 250 if (val > RCSNUM_MAXNUM) 251 fatal("RCSNUM overflow!"); 252 253 nump->rn_id[nump->rn_len] = val; 254 } 255 256 if (ep != NULL) 257 *(const char **)ep = sp; 258 259 /* 260 * Handle "magic" RCS branch numbers. 261 * 262 * What are they? 263 * 264 * Magic branch numbers have an extra .0. at the second farmost 265 * rightside of the branch number, so instead of having an odd 266 * number of dot-separated decimals, it will have an even number. 267 * 268 * Now, according to all the documentation i've found on the net 269 * about this, cvs does this for "efficiency reasons", i'd like 270 * to hear one. 271 * 272 * We just make sure we remove the .0. from in the branch number. 273 * 274 * XXX - for compatibility reasons with GNU cvs we _need_ 275 * to skip this part for the 'log' command, apparently it does 276 * show the magic branches for an unknown and probably 277 * completely insane and not understandable reason in that output. 278 * 279 */ 280 if (nump->rn_len > 2 && nump->rn_id[nump->rn_len - 1] == 0 281 && !(rcsnum_flags & RCSNUM_NO_MAGIC)) { 282 /* 283 * Look for ".0.x" at the end of the branch number. 284 */ 285 if ((s = strrchr(str, '.')) != NULL) { 286 s--; 287 while (*s != '.') 288 s--; 289 290 /* 291 * If we have a "magic" branch, adjust it 292 * so the .0. is removed. 293 */ 294 if (!strncmp(s, RCS_MAGIC_BRANCH, 295 strlen(RCS_MAGIC_BRANCH))) { 296 nump->rn_id[nump->rn_len - 1] = 297 nump->rn_id[nump->rn_len]; 298 nump->rn_len--; 299 } 300 } 301 } 302 303 /* We can't have a single-digit rcs number. */ 304 if (nump->rn_len == 0) { 305 tmp = xrealloc(nump->rn_id, 306 nump->rn_len + 1, sizeof(*(nump->rn_id))); 307 nump->rn_id = tmp; 308 nump->rn_id[nump->rn_len + 1] = 0; 309 nump->rn_len++; 310 } 311 312 nump->rn_len++; 313 return (nump->rn_len); 314 315rcsnum_aton_failed: 316 nump->rn_len = 0; 317 xfree(nump->rn_id); 318 nump->rn_id = NULL; 319 return (-1); 320} 321 322/* 323 * rcsnum_inc() 324 * 325 * Increment the revision number specified in <num>. 326 * Returns a pointer to the <num> on success, or NULL on failure. 327 */ 328RCSNUM * 329rcsnum_inc(RCSNUM *num) 330{ 331 if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM) 332 return (NULL); 333 num->rn_id[num->rn_len - 1]++; 334 return (num); 335} 336 337/* 338 * rcsnum_dec() 339 * 340 * Decreases the revision number specified in <num>, if doing so will not 341 * result in an ending value below 1. E.g. 4.2 will go to 4.1 but 4.1 will 342 * be returned as 4.1. 343 */ 344RCSNUM * 345rcsnum_dec(RCSNUM *num) 346{ 347 /* XXX - Is it an error for the number to be 0? */ 348 if (num->rn_id[num->rn_len - 1] <= 1) 349 return (num); 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 */ 361RCSNUM * 362rcsnum_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 */ 384RCSNUM * 385rcsnum_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 401static void 402rcsnum_setsize(RCSNUM *num, u_int len) 403{ 404 void *tmp; 405 406 tmp = xrealloc(num->rn_id, len, sizeof(*(num->rn_id))); 407 num->rn_id = tmp; 408 num->rn_len = len; 409} 410 411int 412rcsnum_differ(RCSNUM *r1, RCSNUM *r2) 413{ 414 int i, len; 415 416 if (r1->rn_len != r2->rn_len) 417 return (1); 418 419 len = MIN(r1->rn_len, r2->rn_len); 420 for (i = 0; i < len; i++) { 421 if (r1->rn_id[i] != r2->rn_id[i]) 422 return (1); 423 } 424 425 return (0); 426} 427