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