rcsnum.c revision 1.31
1/* $OpenBSD: rcsnum.c,v 1.31 2006/03/30 23:06:25 joris 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 int rcsnum_setsize(RCSNUM *, u_int); 35static char *rcsnum_itoa(u_int16_t, char *, size_t); 36 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 = (RCSNUM *)xmalloc(sizeof(*rnp)); 49 rnp->rn_len = 0; 50 rnp->rn_id = NULL; 51 52 return (rnp); 53} 54 55/* 56 * rcsnum_parse() 57 * 58 * Parse a string specifying an RCS number and return the corresponding RCSNUM. 59 */ 60RCSNUM * 61rcsnum_parse(const char *str) 62{ 63 char *ep; 64 RCSNUM *num; 65 66 num = rcsnum_alloc(); 67 if ((rcsnum_aton(str, &ep, num) < 0) || (*ep != '\0')) { 68 rcsnum_free(num); 69 num = NULL; 70 if (*ep != '\0') 71 rcs_errno = RCS_ERR_BADNUM; 72 } 73 74 return (num); 75} 76 77/* 78 * rcsnum_free() 79 * 80 * Free an RCSNUM structure previously allocated with rcsnum_alloc(). 81 */ 82void 83rcsnum_free(RCSNUM *rn) 84{ 85 if (rn->rn_id != NULL) 86 xfree(rn->rn_id); 87 xfree(rn); 88} 89 90/* 91 * rcsnum_tostr() 92 * 93 * Format the RCS number <nump> into a human-readable dot-separated 94 * representation and store the resulting string in <buf>, which is of size 95 * <blen>. 96 * Returns a pointer to the start of <buf>. On failure <buf> is set to 97 * an empty string. 98 */ 99char * 100rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen) 101{ 102 u_int i; 103 char tmp[8]; 104 105 if ((nump == NULL) || (nump->rn_len == 0)) { 106 buf[0] = '\0'; 107 return (buf); 108 } 109 110 strlcpy(buf, rcsnum_itoa(nump->rn_id[0], buf, blen), blen); 111 for (i = 1; i < nump->rn_len; i++) { 112 strlcat(buf, ".", blen); 113 strlcat(buf, rcsnum_itoa(nump->rn_id[i], tmp, sizeof(tmp)), 114 blen); 115 } 116 117 return (buf); 118} 119 120static char * 121rcsnum_itoa(u_int16_t num, char *buf, size_t len) 122{ 123 u_int16_t i; 124 char *p; 125 126 if (num == 0) 127 return "0"; 128 129 p = buf + len - 1; 130 i = num; 131 bzero(buf, len); 132 while (i) { 133 *--p = '0' + (i % 10); 134 i /= 10; 135 } 136 return (p); 137} 138 139/* 140 * rcsnum_cpy() 141 * 142 * Copy the number stored in <nsrc> in the destination <ndst> up to <depth> 143 * numbers deep. If <depth> is 0, there is no depth limit. 144 */ 145void 146rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth) 147{ 148 u_int len; 149 void *tmp; 150 151 len = nsrc->rn_len; 152 if ((depth != 0) && (len > depth)) 153 len = depth; 154 155 tmp = xrealloc(ndst->rn_id, len, sizeof(len)); 156 ndst->rn_id = (u_int16_t *)tmp; 157 ndst->rn_len = len; 158 /* Overflow checked in xrealloc(). */ 159 memcpy(ndst->rn_id, nsrc->rn_id, len * sizeof(len)); 160} 161 162/* 163 * rcsnum_cmp() 164 * 165 * Compare the two numbers <n1> and <n2>. Returns -1 if <n1> is larger than 166 * <n2>, 0 if they are both the same, and 1 if <n2> is larger than <n1>. 167 * The <depth> argument specifies how many numbers deep should be checked for 168 * the result. A value of 0 means that the depth will be the minimum of the 169 * two numbers. 170 */ 171int 172rcsnum_cmp(const RCSNUM *n1, const RCSNUM *n2, u_int depth) 173{ 174 int res; 175 u_int i; 176 size_t slen; 177 178 slen = MIN(n1->rn_len, n2->rn_len); 179 if ((depth != 0) && (slen > depth)) 180 slen = depth; 181 182 for (i = 0; i < slen; i++) { 183 res = n1->rn_id[i] - n2->rn_id[i]; 184 if (res < 0) 185 return (1); 186 else if (res > 0) 187 return (-1); 188 } 189 190 if (n1->rn_len > n2->rn_len) 191 return (-1); 192 else if (n2->rn_len > n1->rn_len) 193 return (1); 194 195 return (0); 196} 197 198/* 199 * rcsnum_aton() 200 * 201 * Translate the string <str> containing a sequence of digits and periods into 202 * its binary representation, which is stored in <nump>. The address of the 203 * first byte not part of the number is stored in <ep> on return, if it is not 204 * NULL. 205 * Returns 0 on success, or -1 on failure. 206 */ 207int 208rcsnum_aton(const char *str, char **ep, RCSNUM *nump) 209{ 210 u_int32_t val; 211 const char *sp; 212 void *tmp; 213 char *s; 214 215 if (nump->rn_id == NULL) 216 nump->rn_id = (u_int16_t *)xmalloc(sizeof(u_int16_t)); 217 218 nump->rn_len = 0; 219 nump->rn_id[0] = 0; 220 221 for (sp = str;; sp++) { 222 if (!isdigit(*sp) && (*sp != '.')) 223 break; 224 225 if (*sp == '.') { 226 if (nump->rn_len >= RCSNUM_MAXLEN - 1) { 227 rcs_errno = RCS_ERR_BADNUM; 228 goto rcsnum_aton_failed; 229 } 230 231 nump->rn_len++; 232 tmp = xrealloc(nump->rn_id, 233 nump->rn_len + 1, sizeof(u_int16_t)); 234 nump->rn_id = (u_int16_t *)tmp; 235 nump->rn_id[nump->rn_len] = 0; 236 continue; 237 } 238 239 val = (nump->rn_id[nump->rn_len] * 10) + (*sp - 0x30); 240 if (val > RCSNUM_MAXNUM) 241 fatal("RCSNUM overflow!"); 242 243 nump->rn_id[nump->rn_len] = val; 244 } 245 246 if (ep != NULL) 247 *(const char **)ep = sp; 248 249 /* 250 * Handle "magic" RCS branch numbers. 251 * 252 * What are they? 253 * 254 * Magic branch numbers have an extra .0. at the second farmost 255 * rightside of the branch number, so instead of having an odd 256 * number of dot-separated decimals, it will have an even number. 257 * 258 * Now, according to all the documentation i've found on the net 259 * about this, cvs does this for "efficiency reasons", i'd like 260 * to hear one. 261 * 262 * We just make sure we remove the .0. from in the branch number. 263 * 264 * XXX - for compatibility reasons with GNU cvs we _need_ 265 * to skip this part for the 'log' command, apparently it does 266 * show the magic branches for an unknown and probably 267 * completely insane and not understandable reason in that output. 268 * 269 */ 270#if !defined(RCSPROG) 271 if ((nump->rn_len > 2) && (nump->rn_id[nump->rn_len - 1] == 0) 272 && (cvs_cmdop != CVS_OP_LOG)) { 273#else 274 if ((nump->rn_len > 2) && (nump->rn_id[nump->rn_len - 1] == 0)) { 275#endif 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(u_int16_t)); 301 nump->rn_id = (u_int16_t *)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 if (rcsnum_setsize(num, brnum->rn_len + 1) < 0) { 389 rcsnum_free(num); 390 return (NULL); 391 } 392 393 rcsnum_cpy(brnum, num, brnum->rn_len); 394 num->rn_id[num->rn_len++] = 1; 395 396 return (num); 397} 398 399static int 400rcsnum_setsize(RCSNUM *num, u_int len) 401{ 402 void *tmp; 403 404 tmp = xrealloc(num->rn_id, len, sizeof(u_int16_t)); 405 num->rn_id = (u_int16_t *)tmp; 406 num->rn_len = len; 407 return (0); 408} 409