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