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