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