rcsnum.c revision 1.13
1/* $OpenBSD: rcsnum.c,v 1.13 2005/07/25 12:05:43 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 <sys/param.h> 28 29#include <ctype.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33 34#include "log.h" 35#include "rcs.h" 36 37 38static int rcsnum_setsize (RCSNUM *, u_int); 39 40 41/* 42 * rcsnum_alloc() 43 * 44 * Allocate an RCS number structure and return a pointer to it on success, 45 * or NULL on failure. 46 */ 47RCSNUM* 48rcsnum_alloc(void) 49{ 50 RCSNUM *rnp; 51 52 rnp = (RCSNUM *)malloc(sizeof(*rnp)); 53 if (rnp == NULL) { 54 rcs_errno = RCS_ERR_ERRNO; 55 return (NULL); 56 } 57 rnp->rn_len = 0; 58 rnp->rn_id = NULL; 59 60 return (rnp); 61} 62 63/* 64 * rcsnum_parse() 65 * 66 * Parse a string specifying an RCS number and return the corresponding RCSNUM. 67 */ 68RCSNUM* 69rcsnum_parse(const char *str) 70{ 71 char *ep; 72 RCSNUM *num; 73 74 if ((num = rcsnum_alloc()) == NULL) 75 return (NULL); 76 77 if ((rcsnum_aton(str, &ep, num) < 0) || (*ep != '\0')) { 78 rcsnum_free(num); 79 num = NULL; 80 if (*ep != '\0') 81 rcs_errno = RCS_ERR_BADNUM; 82 } 83 84 return (num); 85} 86 87/* 88 * rcsnum_free() 89 * 90 * Free an RCSNUM structure previously allocated with rcsnum_alloc(). 91 */ 92void 93rcsnum_free(RCSNUM *rn) 94{ 95 if (rn->rn_id != NULL) 96 free(rn->rn_id); 97 free(rn); 98} 99 100/* 101 * rcsnum_tostr() 102 * 103 * Format the RCS number <nump> into a human-readable dot-separated 104 * representation and store the resulting string in <buf>, which is of size 105 * <blen>. 106 * Returns a pointer to the start of <buf> on success, or NULL on failure. 107 */ 108char * 109rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen) 110{ 111 u_int i; 112 char tmp[8]; 113 114 if ((nump == NULL) || (nump->rn_len == 0)) { 115 buf[0] = '\0'; 116 return (buf); 117 } 118 119 snprintf(buf, blen, "%u", nump->rn_id[0]); 120 for (i = 1; i < nump->rn_len; i++) { 121 snprintf(tmp, sizeof(tmp), ".%u", nump->rn_id[i]); 122 strlcat(buf, tmp, blen); 123 } 124 125 return (buf); 126} 127 128/* 129 * rcsnum_cpy() 130 * 131 * Copy the number stored in <nsrc> in the destination <ndst> up to <depth> 132 * numbers deep. 133 * Returns 0 on success, or -1 on failure. 134 */ 135int 136rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth) 137{ 138 u_int len; 139 size_t sz; 140 void *tmp; 141 142 len = nsrc->rn_len; 143 if ((depth != 0) && (len > depth)) 144 len = depth; 145 sz = len * sizeof(u_int16_t); 146 147 tmp = realloc(ndst->rn_id, sz); 148 if (tmp == NULL) { 149 rcs_errno = RCS_ERR_ERRNO; 150 return (-1); 151 } 152 153 ndst->rn_id = (u_int16_t *)tmp; 154 ndst->rn_len = len; 155 memcpy(ndst->rn_id, nsrc->rn_id, sz); 156 return (0); 157} 158 159/* 160 * rcsnum_cmp() 161 * 162 * Compare the two numbers <n1> and <n2>. Returns -1 if <n1> is larger than 163 * <n2>, 0 if they are both the same, and 1 if <n2> is larger than <n1>. 164 * The <depth> argument specifies how many numbers deep should be checked for 165 * the result. A value of 0 means that the depth will be the minimum of the 166 * two numbers. 167 */ 168int 169rcsnum_cmp(const RCSNUM *n1, const RCSNUM *n2, u_int depth) 170{ 171 int res; 172 u_int i; 173 size_t slen; 174 175 slen = MIN(n1->rn_len, n2->rn_len); 176 if ((depth != 0) && (slen > depth)) 177 slen = depth; 178 179 for (i = 0; i < slen; i++) { 180 res = n1->rn_id[i] - n2->rn_id[i]; 181 if (res < 0) 182 return (1); 183 else if (res > 0) 184 return (-1); 185 } 186 187 if (n1->rn_len > n2->rn_len) 188 return (-1); 189 else if (n2->rn_len > n1->rn_len) 190 return (1); 191 192 return (0); 193} 194 195/* 196 * rcsnum_aton() 197 * 198 * Translate the string <str> containing a sequence of digits and periods into 199 * its binary representation, which is stored in <nump>. The address of the 200 * first byte not part of the number is stored in <ep> on return, if it is not 201 * NULL. 202 * Returns 0 on success, or -1 on failure. 203 */ 204int 205rcsnum_aton(const char *str, char **ep, RCSNUM *nump) 206{ 207 u_int32_t val; 208 const char *sp; 209 void *tmp; 210 211 if (nump->rn_id == NULL) { 212 nump->rn_id = (u_int16_t *)malloc(sizeof(u_int16_t)); 213 if (nump->rn_id == NULL) { 214 rcs_errno = RCS_ERR_ERRNO; 215 return (-1); 216 } 217 } 218 219 nump->rn_len = 0; 220 nump->rn_id[0] = 0; 221 222 for (sp = str;; sp++) { 223 if (!isdigit(*sp) && (*sp != '.')) 224 break; 225 226 if (*sp == '.') { 227 if (nump->rn_len >= RCSNUM_MAXLEN - 1) { 228 rcs_errno = RCS_ERR_BADNUM; 229 goto rcsnum_aton_failed; 230 } 231 232 nump->rn_len++; 233 tmp = realloc(nump->rn_id, 234 (nump->rn_len + 1) * sizeof(u_int16_t)); 235 if (tmp == NULL) 236 goto rcsnum_aton_failed; 237 nump->rn_id = (u_int16_t *)tmp; 238 nump->rn_id[nump->rn_len] = 0; 239 continue; 240 } 241 242 val = (nump->rn_id[nump->rn_len] * 10) + (*sp - 0x30); 243 if (val > RCSNUM_MAXNUM) { 244 cvs_log(LP_ERR, "RCSNUM overflow"); 245 goto rcsnum_aton_failed; 246 } 247 248 nump->rn_id[nump->rn_len] = val; 249 } 250 251 if (ep != NULL) 252 *(const char **)ep = sp; 253 254 nump->rn_len++; 255 return (nump->rn_len); 256 257rcsnum_aton_failed: 258 nump->rn_len = 0; 259 free(nump->rn_id); 260 nump->rn_id = NULL; 261 return (-1); 262} 263 264/* 265 * rcsnum_inc() 266 * 267 * Increment the revision number specified in <num>. 268 * Returns a pointer to the <num> on success, or NULL on failure. 269 */ 270RCSNUM* 271rcsnum_inc(RCSNUM *num) 272{ 273 if (num->rn_id[num->rn_len - 1] == RCSNUM_MAXNUM) 274 return (NULL); 275 num->rn_id[num->rn_len - 1]++; 276 return (num); 277} 278 279/* 280 * rcsnum_revtobr() 281 * 282 * Retrieve the branch number associated with the revision number <num>. 283 * If <num> is a branch revision, the returned value will be the same 284 * number as the argument. 285 */ 286RCSNUM* 287rcsnum_revtobr(const RCSNUM *num) 288{ 289 RCSNUM *brnum; 290 291 if (num->rn_len < 2) 292 return (NULL); 293 294 if ((brnum = rcsnum_alloc()) == NULL) 295 return (NULL); 296 297 rcsnum_cpy(num, brnum, 0); 298 299 if (!RCSNUM_ISBRANCH(brnum)) 300 brnum->rn_len--; 301 302 return (brnum); 303} 304 305/* 306 * rcsnum_brtorev() 307 * 308 * Retrieve the initial revision number associated with the branch number <num>. 309 * If <num> is a revision number, an error will be returned. 310 */ 311RCSNUM* 312rcsnum_brtorev(const RCSNUM *brnum) 313{ 314 RCSNUM *num; 315 316 if (!RCSNUM_ISBRANCH(brnum)) { 317 return (NULL); 318 } 319 320 if ((num = rcsnum_alloc()) == NULL) 321 return (NULL); 322 323 if (rcsnum_setsize(num, brnum->rn_len + 1) < 0) { 324 rcsnum_free(num); 325 return (NULL); 326 } 327 328 rcsnum_cpy(brnum, num, brnum->rn_len); 329 num->rn_id[num->rn_len++] = 1; 330 331 return (num); 332} 333 334static int 335rcsnum_setsize(RCSNUM *num, u_int len) 336{ 337 void *tmp; 338 339 tmp = realloc(num->rn_id, len * sizeof(u_int16_t)); 340 if (tmp == NULL) { 341 rcs_errno = RCS_ERR_ERRNO; 342 return (-1); 343 } 344 345 num->rn_id = (u_int16_t *)tmp; 346 num->rn_len = len; 347 return (0); 348} 349