1 /*
2  * libiio - Library for interfacing industrial I/O (IIO) devices
3  *
4  * Copyright (C) 2014 Analog Devices, Inc.
5  * Author: Paul Cercueil <paul.cercueil@analog.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * */
18 
19 /* Force the XSI version of strerror_r */
20 #undef _GNU_SOURCE
21 
22 #include "iio-config.h"
23 #include "iio-private.h"
24 
25 #include <errno.h>
26 #include <locale.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #if defined(_WIN32) || (defined(__USE_XOPEN2K8) && \
32 		(!defined(__UCLIBC__) || defined(__UCLIBC_HAS_LOCALE__)))
33 #define LOCALE_SUPPORT
34 #endif
35 
36 #ifdef LOCALE_SUPPORT
37 #if defined(__MINGW32__) || (!defined(_WIN32) && !defined(HAS_NEWLOCALE))
read_double_locale(const char * str,double * val)38 static int read_double_locale(const char *str, double *val)
39 {
40 	char *end, *old_locale;
41 	double value;
42 
43 	/* XXX: This is not thread-safe, but it's the only way we have to
44 	 * support locales under MinGW without linking with Visual Studio
45 	 * libraries. */
46 	old_locale = iio_strdup(setlocale(LC_NUMERIC, NULL));
47 	if (!old_locale)
48 		return -ENOMEM;
49 
50 	setlocale(LC_NUMERIC, "C");
51 	value = strtod(str, &end);
52 	setlocale(LC_NUMERIC, old_locale);
53 	free(old_locale);
54 
55 	if (end == str)
56 		return -EINVAL;
57 
58 	*val = value;
59 	return 0;
60 }
61 
write_double_locale(char * buf,size_t len,double val)62 static int write_double_locale(char *buf, size_t len, double val)
63 {
64 	/* XXX: Not thread-safe, see above */
65 	char *old_locale = iio_strdup(setlocale(LC_NUMERIC, NULL));
66 	if (!old_locale)
67 		return -ENOMEM;
68 
69 	setlocale(LC_NUMERIC, "C");
70 	iio_snprintf(buf, len, "%f", val);
71 	setlocale(LC_NUMERIC, old_locale);
72 	free(old_locale);
73 	return 0;
74 }
75 #elif defined(_WIN32)
read_double_locale(const char * str,double * val)76 static int read_double_locale(const char *str, double *val)
77 {
78 	char *end;
79 	double value;
80 	_locale_t locale = _create_locale(LC_NUMERIC, "C");
81 	if (!locale)
82 		return -ENOMEM;
83 
84 	value = _strtod_l(str, &end, locale);
85 	_free_locale(locale);
86 
87 	if (end == str)
88 		return -EINVAL;
89 
90 	*val = value;
91 	return 0;
92 }
93 
write_double_locale(char * buf,size_t len,double val)94 static int write_double_locale(char *buf, size_t len, double val)
95 {
96 	_locale_t locale = _create_locale(LC_NUMERIC, "C");
97 	if (!locale)
98 		return -ENOMEM;
99 
100 	_snprintf_l(buf, len, "%f", locale, val);
101 	_free_locale(locale);
102 	return 0;
103 }
104 #else
read_double_locale(const char * str,double * val)105 static int read_double_locale(const char *str, double *val)
106 {
107 	char *end;
108 	double value;
109 	locale_t old_locale, new_locale;
110 
111 	new_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
112 	if (!new_locale)
113 		return -errno;
114 
115 	old_locale = uselocale(new_locale);
116 
117 	value = strtod(str, &end);
118 	uselocale(old_locale);
119 	freelocale(new_locale);
120 
121 	if (end == str)
122 		return -EINVAL;
123 
124 	*val = value;
125 	return 0;
126 }
127 
write_double_locale(char * buf,size_t len,double val)128 static int write_double_locale(char *buf, size_t len, double val)
129 {
130 	locale_t old_locale, new_locale;
131 
132 	new_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
133 	if (!new_locale)
134 		return -errno;
135 
136 	old_locale = uselocale(new_locale);
137 
138 	iio_snprintf(buf, len, "%f", val);
139 
140 	uselocale(old_locale);
141 	freelocale(new_locale);
142 	return 0;
143 }
144 #endif
145 #endif
146 
read_double(const char * str,double * val)147 int read_double(const char *str, double *val)
148 {
149 #ifdef LOCALE_SUPPORT
150 	return read_double_locale(str, val);
151 #else
152 	char *end;
153 	double value = strtod(str, &end);
154 
155 	if (end == str)
156 		return -EINVAL;
157 
158 	*val = value;
159 	return 0;
160 #endif
161 }
162 
write_double(char * buf,size_t len,double val)163 int write_double(char *buf, size_t len, double val)
164 {
165 #ifdef LOCALE_SUPPORT
166 	return write_double_locale(buf, len, val);
167 #else
168 	iio_snprintf(buf, len, "%f", val);
169 	return 0;
170 #endif
171 }
172 
iio_library_get_version(unsigned int * major,unsigned int * minor,char git_tag[8])173 void iio_library_get_version(unsigned int *major,
174 		unsigned int *minor, char git_tag[8])
175 {
176 	if (major)
177 		*major = LIBIIO_VERSION_MAJOR;
178 	if (minor)
179 		*minor = LIBIIO_VERSION_MINOR;
180 	if (git_tag) {
181 		strncpy(git_tag, LIBIIO_VERSION_GIT, 8);
182 		git_tag[7] = '\0';
183 	}
184 }
185 
iio_strerror(int err,char * buf,size_t len)186 void iio_strerror(int err, char *buf, size_t len)
187 {
188 #if defined(_WIN32)
189 	int ret = strerror_s(buf, len, err);
190 #elif defined(HAS_STRERROR_R)
191 	int ret = strerror_r(err, buf, len);
192 #else
193 	/* no strerror_s, no strerror_r... just use the default message */
194 	int ret = 0xf7de;
195 #endif
196 	if (ret != 0)
197 		iio_snprintf(buf, len, "Unknown error %i", err);
198 }
199 
iio_strdup(const char * str)200 char *iio_strdup(const char *str)
201 {
202 #if defined(_WIN32)
203 	return _strdup(str);
204 #elif defined(HAS_STRDUP)
205 	return strdup(str);
206 #else
207 	size_t len = strlen(str);
208 	char *buf = malloc(len + 1);
209 
210 	if (buf)
211 		memcpy(buf, str, len + 1);
212 	return buf;
213 #endif
214 }
215