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