1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include "libuutil_common.h" 28 29 #include <limits.h> 30 #include <ctype.h> 31 32 #define MAX_BASE 36 33 34 #define IS_DIGIT(x) ((x) >= '0' && (x) <= '9') 35 36 #define CTOI(x) (((x) >= '0' && (x) <= '9') ? (x) - '0' : \ 37 ((x) >= 'a' && (x) <= 'z') ? (x) + 10 - 'a' : (x) + 10 - 'A') 38 39 static int 40 strtoint(const char *s_arg, uint64_t *out, uint32_t base, int sign) 41 { 42 const unsigned char *s = (const unsigned char *)s_arg; 43 44 uint64_t val = 0; 45 uint64_t multmax; 46 47 unsigned c, i; 48 49 int neg = 0; 50 51 int bad_digit = 0; 52 int bad_char = 0; 53 int overflow = 0; 54 55 if (s == NULL || base == 1 || base > MAX_BASE) { 56 uu_set_error(UU_ERROR_INVALID_ARGUMENT); 57 return (-1); 58 } 59 60 while ((c = *s) != 0 && isspace(c)) 61 s++; 62 63 switch (c) { 64 case '-': 65 if (!sign) 66 overflow = 1; /* becomes underflow below */ 67 neg = 1; 68 /*FALLTHRU*/ 69 case '+': 70 c = *++s; 71 break; 72 default: 73 break; 74 } 75 76 if (c == '\0') { 77 uu_set_error(UU_ERROR_EMPTY); 78 return (-1); 79 } 80 81 if (base == 0) { 82 if (c != '0') 83 base = 10; 84 else if (s[1] == 'x' || s[1] == 'X') 85 base = 16; 86 else 87 base = 8; 88 } 89 90 if (base == 16 && c == '0' && (s[1] == 'x' || s[1] == 'X')) 91 c = *(s += 2); 92 93 if ((val = CTOI(c)) >= base) { 94 if (IS_DIGIT(c)) 95 bad_digit = 1; 96 else 97 bad_char = 1; 98 val = 0; 99 } 100 101 multmax = (uint64_t)UINT64_MAX / (uint64_t)base; 102 103 for (c = *++s; c != '\0'; c = *++s) { 104 if ((i = CTOI(c)) >= base) { 105 if (isspace(c)) 106 break; 107 if (IS_DIGIT(c)) 108 bad_digit = 1; 109 else 110 bad_char = 1; 111 i = 0; 112 } 113 114 if (val > multmax) 115 overflow = 1; 116 117 val *= base; 118 if ((uint64_t)UINT64_MAX - val < (uint64_t)i) 119 overflow = 1; 120 121 val += i; 122 } 123 124 while ((c = *s) != 0) { 125 if (!isspace(c)) 126 bad_char = 1; 127 s++; 128 } 129 130 if (sign) { 131 if (neg) { 132 if (val > -(uint64_t)INT64_MIN) 133 overflow = 1; 134 } else { 135 if (val > INT64_MAX) 136 overflow = 1; 137 } 138 } 139 140 if (neg) 141 val = -val; 142 143 if (bad_char | bad_digit | overflow) { 144 if (bad_char) 145 uu_set_error(UU_ERROR_INVALID_CHAR); 146 else if (bad_digit) 147 uu_set_error(UU_ERROR_INVALID_DIGIT); 148 else if (overflow) { 149 if (neg) 150 uu_set_error(UU_ERROR_UNDERFLOW); 151 else 152 uu_set_error(UU_ERROR_OVERFLOW); 153 } 154 return (-1); 155 } 156 157 *out = val; 158 return (0); 159 } 160 161 int 162 uu_strtoint(const char *s, void *v, size_t sz, int base, 163 int64_t min, int64_t max) 164 { 165 uint64_t val_u; 166 int64_t val; 167 168 if (min > max) 169 goto bad_argument; 170 171 switch (sz) { 172 case 1: 173 if (max > INT8_MAX || min < INT8_MIN) 174 goto bad_argument; 175 break; 176 case 2: 177 if (max > INT16_MAX || min < INT16_MIN) 178 goto bad_argument; 179 break; 180 case 4: 181 if (max > INT32_MAX || min < INT32_MIN) 182 goto bad_argument; 183 break; 184 case 8: 185 /* 186 * No range check is needed here as anything that fits into 187 * an int64_t is a valid 8-byte integer. 188 */ 189 break; 190 default: 191 goto bad_argument; 192 } 193 194 if (min == 0 && max == 0) { 195 min = -(1ULL << (8 * sz - 1)); 196 max = (1ULL << (8 * sz - 1)) - 1; 197 } 198 199 if (strtoint(s, &val_u, base, 1) == -1) 200 return (-1); 201 202 val = (int64_t)val_u; 203 204 if (val < min) { 205 uu_set_error(UU_ERROR_UNDERFLOW); 206 return (-1); 207 } else if (val > max) { 208 uu_set_error(UU_ERROR_OVERFLOW); 209 return (-1); 210 } 211 212 switch (sz) { 213 case 1: 214 *(int8_t *)v = val; 215 return (0); 216 case 2: 217 *(int16_t *)v = val; 218 return (0); 219 case 4: 220 *(int32_t *)v = val; 221 return (0); 222 case 8: 223 *(int64_t *)v = val; 224 return (0); 225 default: 226 break; /* fall through to bad_argument */ 227 } 228 229 bad_argument: 230 uu_set_error(UU_ERROR_INVALID_ARGUMENT); 231 return (-1); 232 } 233 234 int 235 uu_strtouint(const char *s, void *v, size_t sz, int base, 236 uint64_t min, uint64_t max) 237 { 238 uint64_t val; 239 240 if (min > max) 241 goto bad_argument; 242 243 switch (sz) { 244 case 1: 245 if (max > UINT8_MAX) 246 goto bad_argument; 247 break; 248 case 2: 249 if (max > UINT16_MAX) 250 goto bad_argument; 251 break; 252 case 4: 253 if (max > UINT32_MAX) 254 goto bad_argument; 255 break; 256 case 8: 257 /* 258 * No range check is needed here as anything that fits into 259 * an int64_t is a valid 8-byte integer. 260 */ 261 break; 262 default: 263 goto bad_argument; 264 } 265 266 if (min == 0 && max == 0) { 267 /* we have to be careful, since << can overflow */ 268 max = (1ULL << (8 * sz - 1)) * 2 - 1; 269 } 270 271 if (strtoint(s, &val, base, 0) == -1) 272 return (-1); 273 274 if (val < min) { 275 uu_set_error(UU_ERROR_UNDERFLOW); 276 return (-1); 277 } else if (val > max) { 278 uu_set_error(UU_ERROR_OVERFLOW); 279 return (-1); 280 } 281 282 switch (sz) { 283 case 1: 284 *(uint8_t *)v = val; 285 return (0); 286 case 2: 287 *(uint16_t *)v = val; 288 return (0); 289 case 4: 290 *(uint32_t *)v = val; 291 return (0); 292 case 8: 293 *(uint64_t *)v = val; 294 return (0); 295 default: 296 break; /* shouldn't happen, fall through */ 297 } 298 299 bad_argument: 300 uu_set_error(UU_ERROR_INVALID_ARGUMENT); 301 return (-1); 302 } 303