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