rcsnum.c revision 1.26
1/* $OpenBSD: rcsnum.c,v 1.26 2006/03/11 22:44:11 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 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 on success, 42 * or NULL on failure. 43 */ 44RCSNUM * 45rcsnum_alloc(void) 46{ 47 RCSNUM *rnp; 48 49 rnp = (RCSNUM *)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. 145 * Returns 0 on success, or -1 on failure. 146 */ 147int 148rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth) 149{ 150 u_int len; 151 size_t sz; 152 void *tmp; 153 154 len = nsrc->rn_len; 155 if ((depth != 0) && (len > depth)) 156 len = depth; 157 sz = len * sizeof(u_int16_t); 158 159 tmp = xrealloc(ndst->rn_id, sz); 160 ndst->rn_id = (u_int16_t *)tmp; 161 ndst->rn_len = len; 162 memcpy(ndst->rn_id, nsrc->rn_id, sz); 163 return (0); 164} 165 166/* 167 * rcsnum_cmp() 168 * 169 * Compare the two numbers <n1> and <n2>. Returns -1 if <n1> is larger than 170 * <n2>, 0 if they are both the same, and 1 if <n2> is larger than <n1>. 171 * The <depth> argument specifies how many numbers deep should be checked for 172 * the result. A value of 0 means that the depth will be the minimum of the 173 * two numbers. 174 */ 175int 176rcsnum_cmp(const RCSNUM *n1, const RCSNUM *n2, u_int depth) 177{ 178 int res; 179 u_int i; 180 size_t slen; 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 = (u_int16_t *)xmalloc(sizeof(u_int16_t)); 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(u_int16_t)); 238 nump->rn_id = (u_int16_t *)tmp; 239 nump->rn_id[nump->rn_len] = 0; 240 continue; 241 } 242 243 val = (nump->rn_id[nump->rn_len] * 10) + (*sp - 0x30); 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 !defined(RCSPROG) 275 if ((nump->rn_len > 2) && (nump->rn_id[nump->rn_len - 1] == 0) 276 && (cvs_cmdop != CVS_OP_LOG)) { 277#else 278 if ((nump->rn_len > 2) && (nump->rn_id[nump->rn_len - 1] == 0)) { 279#endif 280 /* 281 * Look for ".0.x" at the end of the branch number. 282 */ 283 if ((s = strrchr(str, '.')) != NULL) { 284 *s--; 285 while (*s != '.') 286 *s--; 287 288 /* 289 * If we have a "magic" branch, adjust it 290 * so the .0. is removed. 291 */ 292 if (!strncmp(s, RCS_MAGIC_BRANCH, 293 strlen(RCS_MAGIC_BRANCH))) { 294 nump->rn_id[nump->rn_len - 1] = 295 nump->rn_id[nump->rn_len]; 296 nump->rn_len--; 297 } 298 } 299 } 300 301 /* We can't have a single-digit rcs number. */ 302 if (nump->rn_len == 0) { 303 tmp = xrealloc(nump->rn_id, 304 (nump->rn_len + 1) * sizeof(u_int16_t)); 305 nump->rn_id = (u_int16_t *)tmp; 306 nump->rn_id[nump->rn_len + 1] = 0; 307 nump->rn_len++; 308 } 309 nump->rn_len++; 310 return (nump->rn_len); 311 312rcsnum_aton_failed: 313 nump->rn_len = 0; 314 xfree(nump->rn_id); 315 nump->rn_id = NULL; 316 return (-1); 317} 318 319/* 320 * rcsnum_inc() 321 * 322 * Increment the revision number specified in <num>. 323 * Returns a pointer to the <num> on success, or NULL on failure. 324 */ 325RCSNUM * 326rcsnum_inc(RCSNUM *num) 327{ 328 if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM) 329 return (NULL); 330 num->rn_id[num->rn_len - 1]++; 331 return (num); 332} 333 334/* 335 * rcsnum_dec() 336 * 337 * Decreases the revision number specified in <num>, if doing so will not 338 * result in an ending value below 1. E.g. 4.2 will go to 4.1 but 4.1 will 339 * be returned as 4.1. 340 */ 341RCSNUM * 342rcsnum_dec(RCSNUM *num) 343{ 344 if (num->rn_id[num->rn_len - 1] <= 0) 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 if (rcsnum_setsize(num, brnum->rn_len + 1) < 0) { 391 rcsnum_free(num); 392 return (NULL); 393 } 394 395 rcsnum_cpy(brnum, num, brnum->rn_len); 396 num->rn_id[num->rn_len++] = 1; 397 398 return (num); 399} 400 401static int 402rcsnum_setsize(RCSNUM *num, u_int len) 403{ 404 void *tmp; 405 406 tmp = xrealloc(num->rn_id, len * sizeof(u_int16_t)); 407 num->rn_id = (u_int16_t *)tmp; 408 num->rn_len = len; 409 return (0); 410} 411