rcsnum.c revision 1.57
1/* $OpenBSD: rcsnum.c,v 1.57 2015/11/05 09:48:21 nicm 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 <stdlib.h> 29#include <string.h> 30 31#include "cvs.h" 32 33#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 34 35static void rcsnum_setsize(RCSNUM *, u_int); 36static char *rcsnum_itoa(u_int16_t, char *, size_t); 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 = xcalloc(1, sizeof(*rnp)); 49 rnp->rn_len = 0; 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 } 88 89 return (num); 90} 91 92/* 93 * rcsnum_free() 94 * 95 * Free an RCSNUM structure previously allocated with rcsnum_alloc(). 96 */ 97void 98rcsnum_free(RCSNUM *rn) 99{ 100 free(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 */ 112char * 113rcsnum_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 return (buf); 134} 135 136static char * 137rcsnum_itoa(u_int16_t num, char *buf, size_t len) 138{ 139 u_int16_t i; 140 char *p; 141 142 if (num == 0) 143 return "0"; 144 145 p = buf + len - 1; 146 i = num; 147 bzero(buf, len); 148 while (i) { 149 *--p = '0' + (i % 10); 150 i /= 10; 151 } 152 return (p); 153} 154 155/* 156 * rcsnum_cpy() 157 * 158 * Copy the number stored in <nsrc> in the destination <ndst> up to <depth> 159 * numbers deep. If <depth> is 0, there is no depth limit. 160 */ 161void 162rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth) 163{ 164 u_int len; 165 166 len = nsrc->rn_len; 167 if (depth != 0 && len > depth) 168 len = depth; 169 170 rcsnum_setsize(ndst, len); 171 memcpy(ndst->rn_id, nsrc->rn_id, len * sizeof(*(nsrc->rn_id))); 172} 173 174/* 175 * rcsnum_cmp() 176 * 177 * Compare the two numbers <n1> and <n2>. Returns -1 if <n1> is larger than 178 * <n2>, 0 if they are both the same, and 1 if <n2> is larger than <n1>. 179 * The <depth> argument specifies how many numbers deep should be checked for 180 * the result. A value of 0 means that the depth will be the maximum of the 181 * two numbers, so that a longer number is considered greater than a shorter 182 * number if they are equal up to the minimum length. 183 */ 184int 185rcsnum_cmp(RCSNUM *n1, RCSNUM *n2, u_int depth) 186{ 187 int res; 188 u_int i; 189 size_t slen; 190 191 if (!rcsnum_differ(n1, n2)) 192 return (0); 193 194 slen = MINIMUM(n1->rn_len, n2->rn_len); 195 if (depth != 0 && slen > depth) 196 slen = depth; 197 198 for (i = 0; i < slen; i++) { 199 res = n1->rn_id[i] - n2->rn_id[i]; 200 if (res < 0) 201 return (1); 202 else if (res > 0) 203 return (-1); 204 } 205 206 /* If an explicit depth was specified, and we've 207 * already checked up to depth, consider the 208 * revision numbers equal. */ 209 if (depth != 0 && slen == depth) 210 return (0); 211 else if (n1->rn_len > n2->rn_len) 212 return (-1); 213 else if (n2->rn_len > n1->rn_len) 214 return (1); 215 216 return (0); 217} 218 219/* 220 * rcsnum_aton() 221 * 222 * Translate the string <str> containing a sequence of digits and periods into 223 * its binary representation, which is stored in <nump>. The address of the 224 * first byte not part of the number is stored in <ep> on return, if it is not 225 * NULL. 226 * Returns 0 on success, or -1 on failure. 227 */ 228int 229rcsnum_aton(const char *str, char **ep, RCSNUM *nump) 230{ 231 u_int32_t val; 232 const char *sp; 233 char *s; 234 235 nump->rn_len = 0; 236 nump->rn_id[0] = 0; 237 238 for (sp = str;; sp++) { 239 if (!isdigit((unsigned char)*sp) && (*sp != '.')) 240 break; 241 242 if (*sp == '.') { 243 if (nump->rn_len >= RCSNUM_MAXLEN - 1) 244 goto rcsnum_aton_failed; 245 246 nump->rn_len++; 247 nump->rn_id[nump->rn_len] = 0; 248 continue; 249 } 250 251 val = (nump->rn_id[nump->rn_len] * 10) + (*sp - '0'); 252 if (val > RCSNUM_MAXNUM) 253 fatal("RCSNUM overflow!"); 254 255 nump->rn_id[nump->rn_len] = val; 256 } 257 258 if (ep != NULL) 259 *(const char **)ep = sp; 260 261 /* 262 * Handle "magic" RCS branch numbers. 263 * 264 * What are they? 265 * 266 * Magic branch numbers have an extra .0. at the second farmost 267 * rightside of the branch number, so instead of having an odd 268 * number of dot-separated decimals, it will have an even number. 269 * 270 * Now, according to all the documentation I've found on the net 271 * about this, cvs does this for "efficiency reasons", I'd like 272 * to hear one. 273 * 274 * We just make sure we remove the .0. from in the branch number. 275 * 276 * XXX - for compatibility reasons with GNU cvs we _need_ 277 * to skip this part for the 'log' command, apparently it does 278 * show the magic branches for an unknown and probably 279 * completely insane and not understandable reason in that output. 280 * 281 */ 282 if (nump->rn_len > 2 && nump->rn_id[nump->rn_len - 1] == 0) { 283 /* 284 * Look for ".0.x" at the end of the branch number. 285 */ 286 if ((s = strrchr(str, '.')) != NULL) { 287 s--; 288 while (*s != '.') 289 s--; 290 291 /* 292 * If we have a "magic" branch, adjust it 293 * so the .0. is removed. 294 */ 295 if (!strncmp(s, RCS_MAGIC_BRANCH, 296 sizeof(RCS_MAGIC_BRANCH) - 1)) { 297 nump->rn_id[nump->rn_len - 1] = 298 nump->rn_id[nump->rn_len]; 299 nump->rn_len--; 300 } 301 } 302 } 303 304 /* We can't have a single-digit rcs number. */ 305 if (nump->rn_len == 0) { 306 nump->rn_len++; 307 nump->rn_id[nump->rn_len] = 0; 308 } 309 310 nump->rn_len++; 311 return (nump->rn_len); 312 313rcsnum_aton_failed: 314 nump->rn_len = 0; 315 return (-1); 316} 317 318/* 319 * rcsnum_inc() 320 * 321 * Increment the revision number specified in <num>. 322 * Returns a pointer to the <num> on success, or NULL on failure. 323 */ 324RCSNUM * 325rcsnum_inc(RCSNUM *num) 326{ 327 if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM) 328 return (NULL); 329 num->rn_id[num->rn_len - 1]++; 330 return (num); 331} 332 333/* 334 * rcsnum_dec() 335 * 336 * Decreases the revision number specified in <num>, if doing so will not 337 * result in an ending value below 1. E.g. 4.2 will go to 4.1 but 4.1 will 338 * be returned as 4.1. 339 */ 340RCSNUM * 341rcsnum_dec(RCSNUM *num) 342{ 343 /* XXX - Is it an error for the number to be 0? */ 344 if (num->rn_id[num->rn_len - 1] <= 1) 345 return (num); 346 num->rn_id[num->rn_len - 1]--; 347 return (num); 348} 349 350/* 351 * rcsnum_revtobr() 352 * 353 * Retrieve the branch number associated with the revision number <num>. 354 * If <num> is a branch revision, the returned value will be the same 355 * number as the argument. 356 */ 357RCSNUM * 358rcsnum_revtobr(const RCSNUM *num) 359{ 360 RCSNUM *brnum; 361 362 if (num->rn_len < 2) 363 return (NULL); 364 365 brnum = rcsnum_alloc(); 366 rcsnum_cpy(num, brnum, 0); 367 368 if (!RCSNUM_ISBRANCH(brnum)) 369 brnum->rn_len--; 370 371 return (brnum); 372} 373 374/* 375 * rcsnum_brtorev() 376 * 377 * Retrieve the initial revision number associated with the branch number <num>. 378 * If <num> is a revision number, an error will be returned. 379 */ 380RCSNUM * 381rcsnum_brtorev(const RCSNUM *brnum) 382{ 383 RCSNUM *num; 384 385 if (!RCSNUM_ISBRANCH(brnum)) { 386 return (NULL); 387 } 388 389 num = rcsnum_alloc(); 390 rcsnum_setsize(num, brnum->rn_len + 1); 391 rcsnum_cpy(brnum, num, brnum->rn_len); 392 num->rn_id[num->rn_len++] = 1; 393 394 return (num); 395} 396 397RCSNUM * 398rcsnum_new_branch(RCSNUM *rev) 399{ 400 RCSNUM *branch; 401 402 if (rev->rn_len > RCSNUM_MAXLEN - 1) 403 return NULL; 404 405 branch = rcsnum_alloc(); 406 rcsnum_cpy(rev, branch, 0); 407 rcsnum_setsize(branch, rev->rn_len + 1); 408 branch->rn_id[branch->rn_len - 1] = 2; 409 410 return branch; 411} 412 413RCSNUM * 414rcsnum_branch_root(RCSNUM *brev) 415{ 416 RCSNUM *root; 417 418 if (!RCSNUM_ISBRANCHREV(brev)) 419 fatal("rcsnum_branch_root: no revision on branch specified"); 420 421 root = rcsnum_alloc(); 422 rcsnum_cpy(brev, root, 0); 423 root->rn_len -= 2; 424 return (root); 425} 426 427static void 428rcsnum_setsize(RCSNUM *num, u_int len) 429{ 430 num->rn_len = len; 431} 432 433int 434rcsnum_differ(RCSNUM *r1, RCSNUM *r2) 435{ 436 int i, len; 437 438 if (r1->rn_len != r2->rn_len) 439 return (1); 440 441 len = MINIMUM(r1->rn_len, r2->rn_len); 442 for (i = 0; i < len; i++) { 443 if (r1->rn_id[i] != r2->rn_id[i]) 444 return (1); 445 } 446 447 return (0); 448} 449