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