rcsnum.c revision 1.36
1/* $OpenBSD: rcsnum.c,v 1.36 2006/04/14 22:33:15 niallo 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 "includes.h" 28 29#include "cvs.h" 30#include "log.h" 31#include "rcs.h" 32 33 34static void rcsnum_setsize(RCSNUM *, u_int); 35static char *rcsnum_itoa(u_int16_t, char *, size_t); 36 37int rcsnum_flags; 38 39/* 40 * rcsnum_alloc() 41 * 42 * Allocate an RCS number structure and return a pointer to it. 43 */ 44RCSNUM * 45rcsnum_alloc(void) 46{ 47 RCSNUM *rnp; 48 49 rnp = xmalloc(sizeof(*rnp)); 50 rnp->rn_len = 0; 51 rnp->rn_id = NULL; 52 53 return (rnp); 54} 55 56/* 57 * rcsnum_parse() 58 * 59 * Parse a string specifying an RCS number and return the corresponding RCSNUM. 60 */ 61RCSNUM * 62rcsnum_parse(const char *str) 63{ 64 char *ep; 65 RCSNUM *num; 66 67 num = rcsnum_alloc(); 68 if (rcsnum_aton(str, &ep, num) < 0 || *ep != '\0') { 69 rcsnum_free(num); 70 num = NULL; 71 if (*ep != '\0') 72 rcs_errno = RCS_ERR_BADNUM; 73 } 74 75 return (num); 76} 77 78/* 79 * rcsnum_free() 80 * 81 * Free an RCSNUM structure previously allocated with rcsnum_alloc(). 82 */ 83void 84rcsnum_free(RCSNUM *rn) 85{ 86 if (rn->rn_id != NULL) 87 xfree(rn->rn_id); 88 xfree(rn); 89} 90 91/* 92 * rcsnum_tostr() 93 * 94 * Format the RCS number <nump> into a human-readable dot-separated 95 * representation and store the resulting string in <buf>, which is of size 96 * <blen>. 97 * Returns a pointer to the start of <buf>. On failure <buf> is set to 98 * an empty string. 99 */ 100char * 101rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen) 102{ 103 u_int i; 104 char tmp[8]; 105 106 if (nump == NULL || nump->rn_len == 0) { 107 buf[0] = '\0'; 108 return (buf); 109 } 110 111 strlcpy(buf, rcsnum_itoa(nump->rn_id[0], buf, blen), blen); 112 for (i = 1; i < nump->rn_len; i++) { 113 strlcat(buf, ".", blen); 114 strlcat(buf, rcsnum_itoa(nump->rn_id[i], tmp, sizeof(tmp)), 115 blen); 116 } 117 118 return (buf); 119} 120 121static char * 122rcsnum_itoa(u_int16_t num, char *buf, size_t len) 123{ 124 u_int16_t i; 125 char *p; 126 127 if (num == 0) 128 return "0"; 129 130 p = buf + len - 1; 131 i = num; 132 bzero(buf, len); 133 while (i) { 134 *--p = '0' + (i % 10); 135 i /= 10; 136 } 137 return (p); 138} 139 140/* 141 * rcsnum_cpy() 142 * 143 * Copy the number stored in <nsrc> in the destination <ndst> up to <depth> 144 * numbers deep. If <depth> is 0, there is no depth limit. 145 */ 146void 147rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth) 148{ 149 u_int len; 150 void *tmp; 151 152 len = nsrc->rn_len; 153 if (depth != 0 && len > depth) 154 len = depth; 155 156 tmp = xrealloc(ndst->rn_id, len, sizeof(len)); 157 ndst->rn_id = tmp; 158 ndst->rn_len = len; 159 /* Overflow checked in xrealloc(). */ 160 (void)memcpy(ndst->rn_id, nsrc->rn_id, len * sizeof(len)); 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(const RCSNUM *n1, const RCSNUM *n2, u_int depth) 174{ 175 int res; 176 u_int i; 177 size_t slen; 178 179 slen = MIN(n1->rn_len, n2->rn_len); 180 if (depth != 0 && slen > depth) 181 slen = depth; 182 183 for (i = 0; i < slen; i++) { 184 res = n1->rn_id[i] - n2->rn_id[i]; 185 if (res < 0) 186 return (1); 187 else if (res > 0) 188 return (-1); 189 } 190 191 if (n1->rn_len > n2->rn_len) 192 return (-1); 193 else if (n2->rn_len > n1->rn_len) 194 return (1); 195 196 return (0); 197} 198 199/* 200 * rcsnum_aton() 201 * 202 * Translate the string <str> containing a sequence of digits and periods into 203 * its binary representation, which is stored in <nump>. The address of the 204 * first byte not part of the number is stored in <ep> on return, if it is not 205 * NULL. 206 * Returns 0 on success, or -1 on failure. 207 */ 208int 209rcsnum_aton(const char *str, char **ep, RCSNUM *nump) 210{ 211 u_int32_t val; 212 const char *sp; 213 void *tmp; 214 char *s; 215 216 if (nump->rn_id == NULL) 217 nump->rn_id = xmalloc(sizeof(*(nump->rn_id))); 218 219 nump->rn_len = 0; 220 nump->rn_id[0] = 0; 221 222 for (sp = str;; sp++) { 223 if (!isdigit(*sp) && (*sp != '.')) 224 break; 225 226 if (*sp == '.') { 227 if (nump->rn_len >= RCSNUM_MAXLEN - 1) { 228 rcs_errno = RCS_ERR_BADNUM; 229 goto rcsnum_aton_failed; 230 } 231 232 nump->rn_len++; 233 tmp = xrealloc(nump->rn_id, 234 nump->rn_len + 1, sizeof(*(nump->rn_id))); 235 nump->rn_id = tmp; 236 nump->rn_id[nump->rn_len] = 0; 237 continue; 238 } 239 240 val = (nump->rn_id[nump->rn_len] * 10) + (*sp - 0x30); 241 if (val > RCSNUM_MAXNUM) 242 fatal("RCSNUM overflow!"); 243 244 nump->rn_id[nump->rn_len] = val; 245 } 246 247 if (ep != NULL) 248 *(const char **)ep = sp; 249 250 /* 251 * Handle "magic" RCS branch numbers. 252 * 253 * What are they? 254 * 255 * Magic branch numbers have an extra .0. at the second farmost 256 * rightside of the branch number, so instead of having an odd 257 * number of dot-separated decimals, it will have an even number. 258 * 259 * Now, according to all the documentation i've found on the net 260 * about this, cvs does this for "efficiency reasons", i'd like 261 * to hear one. 262 * 263 * We just make sure we remove the .0. from in the branch number. 264 * 265 * XXX - for compatibility reasons with GNU cvs we _need_ 266 * to skip this part for the 'log' command, apparently it does 267 * show the magic branches for an unknown and probably 268 * completely insane and not understandable reason in that output. 269 * 270 */ 271 if (nump->rn_len > 2 && nump->rn_id[nump->rn_len - 1] == 0 272 && !(rcsnum_flags & RCSNUM_NO_MAGIC)) { 273 /* 274 * Look for ".0.x" at the end of the branch number. 275 */ 276 if ((s = strrchr(str, '.')) != NULL) { 277 s--; 278 while (*s != '.') 279 s--; 280 281 /* 282 * If we have a "magic" branch, adjust it 283 * so the .0. is removed. 284 */ 285 if (!strncmp(s, RCS_MAGIC_BRANCH, 286 strlen(RCS_MAGIC_BRANCH))) { 287 nump->rn_id[nump->rn_len - 1] = 288 nump->rn_id[nump->rn_len]; 289 nump->rn_len--; 290 } 291 } 292 } 293 294 /* We can't have a single-digit rcs number. */ 295 if (nump->rn_len == 0) { 296 tmp = xrealloc(nump->rn_id, 297 nump->rn_len + 1, sizeof(*(nump->rn_id))); 298 nump->rn_id = tmp; 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 void *tmp; 396 397 tmp = xrealloc(num->rn_id, len, sizeof(*(num->rn_id))); 398 num->rn_id = tmp; 399 num->rn_len = len; 400} 401