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