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