1 /* 2 * a minimal version of utils.c from libosmocore 3 * 4 * (C) 2011 by Harald Welte <laforge@gnumonks.org> 5 * (C) 2011 by Sylvain Munaut <tnt@246tNt.com> 6 * (C) 2014 by Nils O. SelÄsdal <noselasd@fiane.dyndns.org> 7 * 8 * All Rights Reserved 9 * 10 * SPDX-License-Identifier: GPL-2.0+ 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation; either version 2 of the License, or 15 * (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 */ 23 24 25 #include <stdbool.h> 26 #include <string.h> 27 #include <stdint.h> 28 #include <errno.h> 29 #include <stdio.h> 30 #include <inttypes.h> 31 #include <limits.h> 32 #include <ctype.h> 33 34 #include "utils.h" 35 36 static __thread char namebuf[255]; 37 static const char osmo_identifier_illegal_chars[] = "., {}[]()<>|~\\^`'\"?=;/+*&%$#!"; 38 39 /*! Determine if a given identifier is valid, i.e. doesn't contain illegal chars 40 * \param[in] str String to validate 41 * \param[in] sep_chars Permitted separation characters between identifiers. 42 * \returns true in case \a str contains only valid identifiers and sep_chars, false otherwise 43 */ 44 bool osmo_separated_identifiers_valid(const char *str, const char *sep_chars) 45 { 46 /* characters that are illegal in names */ 47 unsigned int i; 48 size_t len; 49 50 /* an empty string is not a valid identifier */ 51 if (!str || (len = strlen(str)) == 0) 52 return false; 53 54 for (i = 0; i < len; i++) { 55 if (sep_chars && strchr(sep_chars, str[i])) 56 continue; 57 /* check for 7-bit ASCII */ 58 if (str[i] & 0x80) 59 return false; 60 if (!isprint((int)str[i])) 61 return false; 62 /* check for some explicit reserved control characters */ 63 if (strchr(osmo_identifier_illegal_chars, str[i])) 64 return false; 65 } 66 67 return true; 68 } 69 70 /*! Determine if a given identifier is valid, i.e. doesn't contain illegal chars 71 * \param[in] str String to validate 72 * \returns true in case \a str contains valid identifier, false otherwise 73 */ 74 bool osmo_identifier_valid(const char *str) 75 { 76 return osmo_separated_identifiers_valid(str, NULL); 77 } 78 79 /*! Replace characters in the given string buffer so that it is guaranteed to pass osmo_separated_identifiers_valid(). 80 * To guarantee passing osmo_separated_identifiers_valid(), replace_with must not itself be an illegal character. If in 81 * doubt, use '-'. 82 * \param[inout] str Identifier to sanitize, must be nul terminated and in a writable buffer. 83 * \param[in] sep_chars Additional characters that are to be replaced besides osmo_identifier_illegal_chars. 84 * \param[in] replace_with Replace any illegal characters with this character. 85 */ 86 void osmo_identifier_sanitize_buf(char *str, const char *sep_chars, char replace_with) 87 { 88 char *pos; 89 if (!str) 90 return; 91 for (pos = str; *pos; pos++) { 92 if (strchr(osmo_identifier_illegal_chars, *pos) 93 || (sep_chars && strchr(sep_chars, *pos))) 94 *pos = replace_with; 95 } 96 } 97 98 /*! get human-readable string for given value 99 * \param[in] vs Array of value_string tuples 100 * \param[in] val Value to be converted 101 * \returns pointer to human-readable string 102 * 103 * If val is found in vs, the array's string entry is returned. Otherwise, an 104 * "unknown" string containing the actual value is composed in a static buffer 105 * that is reused across invocations. 106 */ 107 const char *get_value_string(const struct value_string *vs, uint32_t val) 108 { 109 const char *str = get_value_string_or_null(vs, val); 110 if (str) 111 return str; 112 113 snprintf(namebuf, sizeof(namebuf), "unknown 0x%"PRIx32, val); 114 namebuf[sizeof(namebuf) - 1] = '\0'; 115 return namebuf; 116 } 117 118 /*! get human-readable string or NULL for given value 119 * \param[in] vs Array of value_string tuples 120 * \param[in] val Value to be converted 121 * \returns pointer to human-readable string or NULL if val is not found 122 */ 123 const char *get_value_string_or_null(const struct value_string *vs, 124 uint32_t val) 125 { 126 int i; 127 128 if (!vs) 129 return NULL; 130 131 for (i = 0;; i++) { 132 if (vs[i].value == 0 && vs[i].str == NULL) 133 break; 134 if (vs[i].value == val) 135 return vs[i].str; 136 } 137 138 return NULL; 139 } 140 141 /*! get numeric value for given human-readable string 142 * \param[in] vs Array of value_string tuples 143 * \param[in] str human-readable string 144 * \returns numeric value (>0) or negative numer in case of error 145 */ 146 int get_string_value(const struct value_string *vs, const char *str) 147 { 148 int i; 149 150 for (i = 0;; i++) { 151 if (vs[i].value == 0 && vs[i].str == NULL) 152 break; 153 if (!strcasecmp(vs[i].str, str)) 154 return vs[i].value; 155 } 156 return -EINVAL; 157 } 158 159 /*! Convert BCD-encoded digit into printable character 160 * \param[in] bcd A single BCD-encoded digit 161 * \returns single printable character 162 */ 163 char osmo_bcd2char(uint8_t bcd) 164 { 165 if (bcd < 0xa) 166 return '' + bcd; 167 else 168 return 'A' + (bcd - 0xa); 169 } 170 171 /*! Convert number in ASCII to BCD value 172 * \param[in] c ASCII character 173 * \returns BCD encoded value of character 174 */ 175 uint8_t osmo_char2bcd(char c) 176 { 177 if (c >= '' && c <= '9') 178 return c - 0x30; 179 else if (c >= 'A' && c <= 'F') 180 return 0xa + (c - 'A'); 181 else if (c >= 'a' && c <= 'f') 182 return 0xa + (c - 'a'); 183 else 184 return 0; 185 } 186 187 /*! Convert BCD to string. 188 * The given nibble offsets are interpreted in BCD order, i.e. nibble 0 is bcd[0] & 0xf, nibble 1 is bcd[0] >> 4, nibble 189 * 3 is bcd[1] & 0xf, etc.. 190 * \param[out] dst Output string buffer, is always nul terminated when dst_size > 0. 191 * \param[in] dst_size sizeof() the output string buffer. 192 * \param[in] bcd Binary coded data buffer. 193 * \param[in] start_nibble Offset to start from, in nibbles, typically 1 to skip the first nibble. 194 * \param[in] end_nibble Offset to stop before, in nibbles, e.g. sizeof(bcd)*2 - (bcd[0] & GSM_MI_ODD? 0:1). 195 * \param[in] allow_hex If false, return error if there are digits other than 0-9. If true, return those as [A-F]. 196 * \returns The strlen that would be written if the output buffer is large enough, excluding nul byte (like 197 * snprintf()), or -EINVAL if allow_hex is false and a digit > 9 is encountered. On -EINVAL, the conversion is 198 * still completed as if allow_hex were passed as true. Return -ENOMEM if dst is NULL or dst_size is zero. 199 * If end_nibble <= start_nibble, write an empty string to dst and return 0. 200 */ 201 int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex) 202 { 203 char *dst_end = dst + dst_size - 1; 204 int nibble_i; 205 int rc = 0; 206 207 if (!dst || dst_size < 1 || start_nibble < 0) 208 return -ENOMEM; 209 210 for (nibble_i = start_nibble; nibble_i < end_nibble && dst < dst_end; nibble_i++, dst++) { 211 uint8_t nibble = bcd[nibble_i >> 1]; 212 if ((nibble_i & 1)) 213 nibble >>= 4; 214 nibble &= 0xf; 215 216 if (!allow_hex && nibble > 9) 217 rc = -EINVAL; 218 219 *dst = osmo_bcd2char(nibble); 220 } 221 *dst = '\0'; 222 223 if (rc < 0) 224 return rc; 225 return OSMO_MAX(0, end_nibble - start_nibble); 226 } 227 228 /*! Convert string to BCD. 229 * The given nibble offsets are interpreted in BCD order, i.e. nibble 0 is bcd[0] & 0x0f, nibble 1 is bcd[0] & 0xf0, nibble 230 * 3 is bcd[1] & 0x0f, etc.. 231 * \param[out] dst Output BCD buffer. 232 * \param[in] dst_size sizeof() the output string buffer. 233 * \param[in] digits String containing decimal or hexadecimal digits in upper or lower case. 234 * \param[in] start_nibble Offset to start from, in nibbles, typically 1 to skip the first (MI type) nibble. 235 * \param[in] end_nibble Negative to write all digits found in str, followed by 0xf nibbles to fill any started octet. 236 * If >= 0, stop before this offset in nibbles, e.g. to get default behavior, pass 237 * start_nibble + strlen(str) + ((start_nibble + strlen(str)) & 1? 1 : 0) + 1. 238 * \param[in] allow_hex If false, return error if there are hexadecimal digits (A-F). If true, write those to 239 * BCD. 240 * \returns The buffer size in octets that is used to place all bcd digits (including the skipped nibbles 241 * from 'start_nibble' and rounded up to full octets); -EINVAL on invalid digits; 242 * -ENOMEM if dst is NULL, if dst_size is too small to contain all nibbles, or if start_nibble is negative. 243 */ 244 int osmo_str2bcd(uint8_t *dst, size_t dst_size, const char *digits, int start_nibble, int end_nibble, bool allow_hex) 245 { 246 const char *digit = digits; 247 int nibble_i; 248 249 if (!dst || !dst_size || start_nibble < 0) 250 return -ENOMEM; 251 252 if (end_nibble < 0) { 253 end_nibble = start_nibble + strlen(digits); 254 /* If the last octet is not complete, add another filler nibble */ 255 if (end_nibble & 1) 256 end_nibble++; 257 } 258 if ((unsigned int) (end_nibble / 2) > dst_size) 259 return -ENOMEM; 260 261 for (nibble_i = start_nibble; nibble_i < end_nibble; nibble_i++) { 262 uint8_t nibble = 0xf; 263 int octet = nibble_i >> 1; 264 if (*digit) { 265 char c = *digit; 266 digit++; 267 if (c >= '' && c <= '9') 268 nibble = c - ''; 269 else if (allow_hex && c >= 'A' && c <= 'F') 270 nibble = 0xa + (c - 'A'); 271 else if (allow_hex && c >= 'a' && c <= 'f') 272 nibble = 0xa + (c - 'a'); 273 else 274 return -EINVAL; 275 } 276 nibble &= 0xf; 277 if ((nibble_i & 1)) 278 dst[octet] = (nibble << 4) | (dst[octet] & 0x0f); 279 else 280 dst[octet] = (dst[octet] & 0xf0) | nibble; 281 } 282 283 /* floor(float(end_nibble) / 2) */ 284 return end_nibble / 2; 285 } 286
This page was automatically generated by LXR 0.3.1. • OpenWrt