rcsnum.c revision 1.46
1/* $OpenBSD: rcsnum.c,v 1.46 2007/09/04 17:57:41 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_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 char *s; 217 218 if (nump->rn_id == NULL) 219 nump->rn_id = xmalloc(sizeof(*(nump->rn_id))); 220 221 nump->rn_len = 0; 222 nump->rn_id[0] = 0; 223 224 for (sp = str;; sp++) { 225 if (!isdigit(*sp) && (*sp != '.')) 226 break; 227 228 if (*sp == '.') { 229 if (nump->rn_len >= RCSNUM_MAXLEN - 1) { 230 rcs_errno = RCS_ERR_BADNUM; 231 goto rcsnum_aton_failed; 232 } 233 234 nump->rn_len++; 235 nump->rn_id = xrealloc(nump->rn_id, 236 nump->rn_len + 1, sizeof(*(nump->rn_id))); 237 nump->rn_id[nump->rn_len] = 0; 238 continue; 239 } 240 241 val = (nump->rn_id[nump->rn_len] * 10) + (*sp - '0'); 242 if (val > RCSNUM_MAXNUM) 243 fatal("RCSNUM overflow!"); 244 245 nump->rn_id[nump->rn_len] = val; 246 } 247 248 if (ep != NULL) 249 *(const char **)ep = sp; 250 251 /* 252 * Handle "magic" RCS branch numbers. 253 * 254 * What are they? 255 * 256 * Magic branch numbers have an extra .0. at the second farmost 257 * rightside of the branch number, so instead of having an odd 258 * number of dot-separated decimals, it will have an even number. 259 * 260 * Now, according to all the documentation I've found on the net 261 * about this, cvs does this for "efficiency reasons", I'd like 262 * to hear one. 263 * 264 * We just make sure we remove the .0. from in the branch number. 265 * 266 * XXX - for compatibility reasons with GNU cvs we _need_ 267 * to skip this part for the 'log' command, apparently it does 268 * show the magic branches for an unknown and probably 269 * completely insane and not understandable reason in that output. 270 * 271 */ 272 if (nump->rn_len > 2 && nump->rn_id[nump->rn_len - 1] == 0 273 && !(rcsnum_flags & RCSNUM_NO_MAGIC)) { 274 /* 275 * Look for ".0.x" at the end of the branch number. 276 */ 277 if ((s = strrchr(str, '.')) != NULL) { 278 s--; 279 while (*s != '.') 280 s--; 281 282 /* 283 * If we have a "magic" branch, adjust it 284 * so the .0. is removed. 285 */ 286 if (!strncmp(s, RCS_MAGIC_BRANCH, 287 sizeof(RCS_MAGIC_BRANCH) - 1)) { 288 nump->rn_id[nump->rn_len - 1] = 289 nump->rn_id[nump->rn_len]; 290 nump->rn_len--; 291 } 292 } 293 } 294 295 /* We can't have a single-digit rcs number. */ 296 if (nump->rn_len == 0) { 297 nump->rn_id = xrealloc(nump->rn_id, 298 nump->rn_len + 1, sizeof(*(nump->rn_id))); 299 nump->rn_id[nump->rn_len + 1] = 0; 300 nump->rn_len++; 301 } 302 303 nump->rn_len++; 304 return (nump->rn_len); 305 306rcsnum_aton_failed: 307 nump->rn_len = 0; 308 xfree(nump->rn_id); 309 nump->rn_id = NULL; 310 return (-1); 311} 312 313/* 314 * rcsnum_inc() 315 * 316 * Increment the revision number specified in <num>. 317 * Returns a pointer to the <num> on success, or NULL on failure. 318 */ 319RCSNUM * 320rcsnum_inc(RCSNUM *num) 321{ 322 if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM) 323 return (NULL); 324 num->rn_id[num->rn_len - 1]++; 325 return (num); 326} 327 328/* 329 * rcsnum_dec() 330 * 331 * Decreases the revision number specified in <num>, if doing so will not 332 * result in an ending value below 1. E.g. 4.2 will go to 4.1 but 4.1 will 333 * be returned as 4.1. 334 */ 335RCSNUM * 336rcsnum_dec(RCSNUM *num) 337{ 338 /* XXX - Is it an error for the number to be 0? */ 339 if (num->rn_id[num->rn_len - 1] <= 1) 340 return (num); 341 num->rn_id[num->rn_len - 1]--; 342 return (num); 343} 344 345/* 346 * rcsnum_revtobr() 347 * 348 * Retrieve the branch number associated with the revision number <num>. 349 * If <num> is a branch revision, the returned value will be the same 350 * number as the argument. 351 */ 352RCSNUM * 353rcsnum_revtobr(const RCSNUM *num) 354{ 355 RCSNUM *brnum; 356 357 if (num->rn_len < 2) 358 return (NULL); 359 360 brnum = rcsnum_alloc(); 361 rcsnum_cpy(num, brnum, 0); 362 363 if (!RCSNUM_ISBRANCH(brnum)) 364 brnum->rn_len--; 365 366 return (brnum); 367} 368 369/* 370 * rcsnum_brtorev() 371 * 372 * Retrieve the initial revision number associated with the branch number <num>. 373 * If <num> is a revision number, an error will be returned. 374 */ 375RCSNUM * 376rcsnum_brtorev(const RCSNUM *brnum) 377{ 378 RCSNUM *num; 379 380 if (!RCSNUM_ISBRANCH(brnum)) { 381 return (NULL); 382 } 383 384 num = rcsnum_alloc(); 385 rcsnum_setsize(num, brnum->rn_len + 1); 386 rcsnum_cpy(brnum, num, brnum->rn_len); 387 num->rn_id[num->rn_len++] = 1; 388 389 return (num); 390} 391 392static void 393rcsnum_setsize(RCSNUM *num, u_int len) 394{ 395 num->rn_id = xrealloc(num->rn_id, len, sizeof(*(num->rn_id))); 396 num->rn_len = len; 397} 398 399int 400rcsnum_differ(RCSNUM *r1, RCSNUM *r2) 401{ 402 int i, len; 403 404 if (r1->rn_len != r2->rn_len) 405 return (1); 406 407 len = MIN(r1->rn_len, r2->rn_len); 408 for (i = 0; i < len; i++) { 409 if (r1->rn_id[i] != r2->rn_id[i]) 410 return (1); 411 } 412 413 return (0); 414} 415