• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/uqmi/uqmid/osmocom/utils.c

  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