1 /* $OpenBSD: rcsnum.c,v 1.40 2006/11/13 18:42:40 xsa 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 static void rcsnum_setsize(RCSNUM *, u_int); 34 static char *rcsnum_itoa(u_int16_t, char *, size_t); 35 36 int rcsnum_flags; 37 38 /* 39 * rcsnum_alloc() 40 * 41 * Allocate an RCS number structure and return a pointer to it. 42 */ 43 RCSNUM * 44 rcsnum_alloc(void) 45 { 46 RCSNUM *rnp; 47 48 rnp = 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 */ 60 RCSNUM * 61 rcsnum_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 */ 82 void 83 rcsnum_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 */ 99 char * 100 rcsnum_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 if (strlcpy(buf, rcsnum_itoa(nump->rn_id[0], buf, blen), blen) >= blen) 111 fatal("rcsnum_tostr: truncation"); 112 for (i = 1; i < nump->rn_len; i++) { 113 const char *str; 114 115 str = rcsnum_itoa(nump->rn_id[i], tmp, sizeof(tmp)); 116 if (strlcat(buf, ".", blen) >= blen || 117 strlcat(buf, str, blen) >= blen) 118 fatal("rcsnum_tostr: truncation"); 119 } 120 121 return (buf); 122 } 123 124 static char * 125 rcsnum_itoa(u_int16_t num, char *buf, size_t len) 126 { 127 u_int16_t i; 128 char *p; 129 130 if (num == 0) 131 return "0"; 132 133 p = buf + len - 1; 134 i = num; 135 bzero(buf, len); 136 while (i) { 137 *--p = '0' + (i % 10); 138 i /= 10; 139 } 140 return (p); 141 } 142 143 /* 144 * rcsnum_cpy() 145 * 146 * Copy the number stored in <nsrc> in the destination <ndst> up to <depth> 147 * numbers deep. If <depth> is 0, there is no depth limit. 148 */ 149 void 150 rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth) 151 { 152 u_int len; 153 void *tmp; 154 155 len = nsrc->rn_len; 156 if (depth != 0 && len > depth) 157 len = depth; 158 159 tmp = xrealloc(ndst->rn_id, len, sizeof(*(nsrc->rn_id))); 160 ndst->rn_id = tmp; 161 ndst->rn_len = len; 162 /* Overflow checked in xrealloc(). */ 163 (void)memcpy(ndst->rn_id, nsrc->rn_id, 164 len * sizeof(*(nsrc->rn_id))); 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 */ 176 int 177 rcsnum_cmp(RCSNUM *n1, RCSNUM *n2, u_int depth) 178 { 179 int res; 180 u_int i; 181 size_t slen; 182 183 if (!rcsnum_differ(n1, n2)) 184 return (0); 185 186 slen = MIN(n1->rn_len, n2->rn_len); 187 if (depth != 0 && slen > depth) 188 slen = depth; 189 190 for (i = 0; i < slen; i++) { 191 res = n1->rn_id[i] - n2->rn_id[i]; 192 if (res < 0) 193 return (1); 194 else if (res > 0) 195 return (-1); 196 } 197 198 if (n1->rn_len > n2->rn_len) 199 return (-1); 200 else if (n2->rn_len > n1->rn_len) 201 return (1); 202 203 return (0); 204 } 205 206 /* 207 * rcsnum_aton() 208 * 209 * Translate the string <str> containing a sequence of digits and periods into 210 * its binary representation, which is stored in <nump>. The address of the 211 * first byte not part of the number is stored in <ep> on return, if it is not 212 * NULL. 213 * Returns 0 on success, or -1 on failure. 214 */ 215 int 216 rcsnum_aton(const char *str, char **ep, RCSNUM *nump) 217 { 218 u_int32_t val; 219 const char *sp; 220 void *tmp; 221 char *s; 222 223 if (nump->rn_id == NULL) 224 nump->rn_id = xmalloc(sizeof(*(nump->rn_id))); 225 226 nump->rn_len = 0; 227 nump->rn_id[0] = 0; 228 229 for (sp = str;; sp++) { 230 if (!isdigit(*sp) && (*sp != '.')) 231 break; 232 233 if (*sp == '.') { 234 if (nump->rn_len >= RCSNUM_MAXLEN - 1) { 235 rcs_errno = RCS_ERR_BADNUM; 236 goto rcsnum_aton_failed; 237 } 238 239 nump->rn_len++; 240 tmp = xrealloc(nump->rn_id, 241 nump->rn_len + 1, sizeof(*(nump->rn_id))); 242 nump->rn_id = tmp; 243 nump->rn_id[nump->rn_len] = 0; 244 continue; 245 } 246 247 val = (nump->rn_id[nump->rn_len] * 10) + (*sp - 0x30); 248 if (val > RCSNUM_MAXNUM) 249 fatal("RCSNUM overflow!"); 250 251 nump->rn_id[nump->rn_len] = val; 252 } 253 254 if (ep != NULL) 255 *(const char **)ep = sp; 256 257 /* 258 * Handle "magic" RCS branch numbers. 259 * 260 * What are they? 261 * 262 * Magic branch numbers have an extra .0. at the second farmost 263 * rightside of the branch number, so instead of having an odd 264 * number of dot-separated decimals, it will have an even number. 265 * 266 * Now, according to all the documentation i've found on the net 267 * about this, cvs does this for "efficiency reasons", i'd like 268 * to hear one. 269 * 270 * We just make sure we remove the .0. from in the branch number. 271 * 272 * XXX - for compatibility reasons with GNU cvs we _need_ 273 * to skip this part for the 'log' command, apparently it does 274 * show the magic branches for an unknown and probably 275 * completely insane and not understandable reason in that output. 276 * 277 */ 278 if (nump->rn_len > 2 && nump->rn_id[nump->rn_len - 1] == 0 279 && !(rcsnum_flags & RCSNUM_NO_MAGIC)) { 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(*(nump->rn_id))); 305 nump->rn_id = tmp; 306 nump->rn_id[nump->rn_len + 1] = 0; 307 nump->rn_len++; 308 } 309 310 nump->rn_len++; 311 return (nump->rn_len); 312 313 rcsnum_aton_failed: 314 nump->rn_len = 0; 315 xfree(nump->rn_id); 316 nump->rn_id = NULL; 317 return (-1); 318 } 319 320 /* 321 * rcsnum_inc() 322 * 323 * Increment the revision number specified in <num>. 324 * Returns a pointer to the <num> on success, or NULL on failure. 325 */ 326 RCSNUM * 327 rcsnum_inc(RCSNUM *num) 328 { 329 if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM) 330 return (NULL); 331 num->rn_id[num->rn_len - 1]++; 332 return (num); 333 } 334 335 /* 336 * rcsnum_dec() 337 * 338 * Decreases the revision number specified in <num>, if doing so will not 339 * result in an ending value below 1. E.g. 4.2 will go to 4.1 but 4.1 will 340 * be returned as 4.1. 341 */ 342 RCSNUM * 343 rcsnum_dec(RCSNUM *num) 344 { 345 /* XXX - Is it an error for the number to be 0? */ 346 if (num->rn_id[num->rn_len - 1] <= 1) 347 return (num); 348 num->rn_id[num->rn_len - 1]--; 349 return (num); 350 } 351 352 /* 353 * rcsnum_revtobr() 354 * 355 * Retrieve the branch number associated with the revision number <num>. 356 * If <num> is a branch revision, the returned value will be the same 357 * number as the argument. 358 */ 359 RCSNUM * 360 rcsnum_revtobr(const RCSNUM *num) 361 { 362 RCSNUM *brnum; 363 364 if (num->rn_len < 2) 365 return (NULL); 366 367 brnum = rcsnum_alloc(); 368 rcsnum_cpy(num, brnum, 0); 369 370 if (!RCSNUM_ISBRANCH(brnum)) 371 brnum->rn_len--; 372 373 return (brnum); 374 } 375 376 /* 377 * rcsnum_brtorev() 378 * 379 * Retrieve the initial revision number associated with the branch number <num>. 380 * If <num> is a revision number, an error will be returned. 381 */ 382 RCSNUM * 383 rcsnum_brtorev(const RCSNUM *brnum) 384 { 385 RCSNUM *num; 386 387 if (!RCSNUM_ISBRANCH(brnum)) { 388 return (NULL); 389 } 390 391 num = rcsnum_alloc(); 392 rcsnum_setsize(num, brnum->rn_len + 1); 393 rcsnum_cpy(brnum, num, brnum->rn_len); 394 num->rn_id[num->rn_len++] = 1; 395 396 return (num); 397 } 398 399 static void 400 rcsnum_setsize(RCSNUM *num, u_int len) 401 { 402 void *tmp; 403 404 tmp = xrealloc(num->rn_id, len, sizeof(*(num->rn_id))); 405 num->rn_id = tmp; 406 num->rn_len = len; 407 } 408 409 int 410 rcsnum_differ(RCSNUM *r1, RCSNUM *r2) 411 { 412 int i, len; 413 414 if (r1->rn_len != r2->rn_len) 415 return (1); 416 417 len = MIN(r1->rn_len, r2->rn_len); 418 for (i = 0; i < len; i++) { 419 if (r1->rn_id[i] != r2->rn_id[i]) 420 return (1); 421 } 422 423 return (0); 424 } 425