rcsnum.c revision 1.9
1/* $OpenBSD: rcsnum.c,v 1.9 2005/03/05 05:58:39 jfb 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 "rcs.h" 35#include "log.h" 36 37 38 39/* 40 * rcsnum_alloc() 41 * 42 * Allocate an RCS number structure. 43 */ 44RCSNUM* 45rcsnum_alloc(void) 46{ 47 RCSNUM *rnp; 48 49 rnp = (RCSNUM *)malloc(sizeof(*rnp)); 50 if (rnp == NULL) { 51 cvs_log(LP_ERR, "failed to allocate RCS number"); 52 return (NULL); 53 } 54 rnp->rn_len = 0; 55 rnp->rn_id = NULL; 56 57 return (rnp); 58} 59 60 61/* 62 * rcsnum_parse() 63 * 64 * Parse a string specifying an RCS number and return the corresponding RCSNUM. 65 */ 66RCSNUM* 67rcsnum_parse(const char *str) 68{ 69 char *ep; 70 RCSNUM *num; 71 72 if ((num = rcsnum_alloc()) == NULL) 73 return (NULL); 74 75 if ((rcsnum_aton(str, &ep, num) < 0) || (*ep != '\0')) { 76 rcsnum_free(num); 77 num = NULL; 78 if (*ep != '\0') 79 rcs_errno = RCS_ERR_BADNUM; 80 } 81 82 return (num); 83} 84 85 86/* 87 * rcsnum_free() 88 * 89 * Free an RCSNUM structure previously allocated with rcsnum_alloc(). 90 */ 91void 92rcsnum_free(RCSNUM *rn) 93{ 94 if (rn->rn_id != NULL) 95 free(rn->rn_id); 96 free(rn); 97} 98 99 100/* 101 * rcsnum_tostr() 102 * Returns a pointer to the start of <buf> on success, or NULL on failure. 103 */ 104char* 105rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen) 106{ 107 u_int i; 108 char tmp[8]; 109 110 if (nump->rn_len == 0) { 111 buf[0] = '\0'; 112 return (buf); 113 } 114 115 snprintf(buf, blen, "%u", nump->rn_id[0]); 116 for (i = 1; i < nump->rn_len; i++) { 117 snprintf(tmp, sizeof(tmp), ".%u", nump->rn_id[i]); 118 strlcat(buf, tmp, blen); 119 } 120 121 return (buf); 122} 123 124 125/* 126 * rcsnum_cpy() 127 * 128 * Copy the number stored in <nsrc> in the destination <ndst> up to <depth> 129 * numbers deep. 130 * Returns 0 on success, or -1 on failure. 131 */ 132int 133rcsnum_cpy(const RCSNUM *nsrc, RCSNUM *ndst, u_int depth) 134{ 135 u_int len; 136 size_t sz; 137 void *tmp; 138 139 len = nsrc->rn_len; 140 if ((depth != 0) && (len > depth)) 141 len = depth; 142 sz = len * sizeof(u_int16_t); 143 144 tmp = realloc(ndst->rn_id, sz); 145 if (tmp == NULL) { 146 cvs_log(LP_ERR, "failed to reallocate RCSNUM"); 147 return (-1); 148 } 149 150 ndst->rn_id = (u_int16_t *)tmp; 151 ndst->rn_len = len; 152 memcpy(ndst->rn_id, nsrc->rn_id, sz); 153 return (0); 154} 155 156 157/* 158 * rcsnum_cmp() 159 * 160 * Compare the two numbers <n1> and <n2>. Returns -1 if <n1> is larger than 161 * <n2>, 0 if they are both the same, and 1 if <n2> is larger than <n1>. 162 * The <depth> argument specifies how many numbers deep should be checked for 163 * the result. A value of 0 means that the depth will be the minimum of the 164 * two numbers. 165 */ 166int 167rcsnum_cmp(const RCSNUM *n1, const RCSNUM *n2, u_int depth) 168{ 169 int res; 170 u_int i; 171 size_t slen; 172 173 slen = MIN(n1->rn_len, n2->rn_len); 174 if ((depth != 0) && (slen > depth)) 175 slen = depth; 176 177 for (i = 0; i < slen; i++) { 178 res = n1->rn_id[i] - n2->rn_id[i]; 179 if (res < 0) 180 return (1); 181 else if (res > 0) 182 return (-1); 183 } 184 185 if (n1->rn_len > n2->rn_len) 186 return (-1); 187 else if (n2->rn_len > n1->rn_len) 188 return (1); 189 190 return (0); 191} 192 193 194/* 195 * rcsnum_aton() 196 * 197 * Translate the string <str> containing a sequence of digits and periods into 198 * its binary representation, which is stored in <nump>. The address of the 199 * first byte not part of the number is stored in <ep> on return, if it is not 200 * NULL. 201 * Returns 0 on success, or -1 on failure. 202 */ 203int 204rcsnum_aton(const char *str, char **ep, RCSNUM *nump) 205{ 206 u_int32_t val; 207 const char *sp; 208 void *tmp; 209 210 if (nump->rn_id == NULL) { 211 nump->rn_id = (u_int16_t *)malloc(sizeof(u_int16_t)); 212 if (nump->rn_id == NULL) { 213 cvs_log(LP_ERRNO, "failed to allocate RCSNUM"); 214 return (-1); 215 } 216 } 217 218 nump->rn_len = 0; 219 nump->rn_id[0] = 0; 220 221 for (sp = str;; sp++) { 222 if (!isdigit(*sp) && (*sp != '.')) 223 break; 224 225 if (*sp == '.') { 226 if (nump->rn_len >= RCSNUM_MAXLEN - 1) { 227 cvs_log(LP_ERR, 228 "RCSNUM exceeds maximum length"); 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