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