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