Add PBD API to hard-link files
[ardour.git] / libs / pbd / string_convert.cc
1 /*
2  * Copyright (C) 2015-2017 Tim Mayberry <mojofunk@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include "pbd/string_convert.h"
20
21 #ifndef __STDC_FORMAT_MACROS
22 #define __STDC_FORMAT_MACROS
23 #endif
24 #include <inttypes.h>
25 #include <cerrno>
26 #include <cstdio>
27 #include <limits>
28
29 #include <glib.h>
30 #include <glib/gprintf.h>
31
32 #include "pbd/compose.h"
33 #include "pbd/debug.h"
34 #include "pbd/i18n.h"
35
36 #define DEBUG_SCONVERT(msg) DEBUG_TRACE (PBD::DEBUG::StringConvert, string_compose ("%1: %2\n", __LINE__, msg));
37
38 #define CONVERT_BUF_SIZE 32
39
40 namespace PBD {
41
42 bool string_to_bool (const std::string& str, bool& val)
43 {
44         if (str.empty ()) {
45                 return false;
46
47         } else if (str == X_("1")) {
48                 val = true;
49                 return true;
50
51         } else if (str == X_("0")) {
52                 val = false;
53                 return true;
54
55         } else if (str == X_("y")) {
56                 val = true;
57                 return true;
58
59         } else if (str == X_("n")) {
60                 val = false;
61                 return true;
62
63         } else if (g_ascii_strncasecmp (str.c_str(), X_("yes"), str.length()) == 0) {
64                 val = true;
65                 return true;
66
67         } else if (g_ascii_strncasecmp (str.c_str(), X_("no"), str.length()) == 0) {
68                 val = false;
69                 return true;
70
71         } else if (g_ascii_strncasecmp (str.c_str(), X_("true"), str.length()) == 0) {
72                 val = true;
73                 return true;
74
75         } else if (g_ascii_strncasecmp (str.c_str(), X_("false"), str.length()) == 0) {
76                 val = false;
77                 return true;
78         }
79
80         DEBUG_SCONVERT (
81             string_compose ("string_to_bool conversion failed for %1", str));
82
83         return false;
84 }
85
86 bool string_to_int16 (const std::string& str, int16_t& val)
87 {
88         if (sscanf (str.c_str (), "%" SCNi16, &val) != 1) {
89                 DEBUG_SCONVERT (
90                     string_compose ("string_to_int16 conversion failed for %1", str));
91                 return false;
92         }
93         return true;
94 }
95
96 bool string_to_uint16 (const std::string& str, uint16_t& val)
97 {
98         if (sscanf (str.c_str (), "%" SCNu16, &val) != 1) {
99                 DEBUG_SCONVERT (
100                     string_compose ("string_to_uint16 conversion failed for %1", str));
101                 return false;
102         }
103         return true;
104 }
105
106 bool string_to_int32 (const std::string& str, int32_t& val)
107 {
108         if (sscanf (str.c_str (), "%" SCNi32, &val) != 1) {
109                 DEBUG_SCONVERT (
110                     string_compose ("string_to_int32 conversion failed for %1", str));
111                 return false;
112         }
113         return true;
114 }
115
116 bool string_to_uint32 (const std::string& str, uint32_t& val)
117 {
118         if (sscanf (str.c_str (), "%" SCNu32, &val) != 1) {
119                 DEBUG_SCONVERT (
120                     string_compose ("string_to_uint32 conversion failed for %1", str));
121                 return false;
122         }
123         return true;
124 }
125
126 bool string_to_int64 (const std::string& str, int64_t& val)
127 {
128         if (sscanf (str.c_str (), "%" SCNi64, &val) != 1) {
129                 DEBUG_SCONVERT (
130                     string_compose ("string_to_int64 conversion failed for %1", str));
131                 return false;
132         }
133         return true;
134 }
135
136 bool string_to_uint64 (const std::string& str, uint64_t& val)
137 {
138         if (sscanf (str.c_str (), "%" SCNu64, &val) != 1) {
139                 DEBUG_SCONVERT (
140                     string_compose ("string_to_uint64 conversion failed for %1", str));
141                 return false;
142         }
143         return true;
144 }
145
146 template <class FloatType>
147 static bool
148 _string_to_infinity (const std::string& str, FloatType& val)
149 {
150         if (!g_ascii_strncasecmp (str.c_str (), X_ ("inf"), str.length ()) ||
151             !g_ascii_strncasecmp (str.c_str (), X_ ("+inf"), str.length ()) ||
152             !g_ascii_strncasecmp (str.c_str (), X_ ("INFINITY"), str.length ()) ||
153             !g_ascii_strncasecmp (str.c_str (), X_ ("+INFINITY"), str.length ())) {
154                 val = std::numeric_limits<FloatType>::infinity ();
155                 return true;
156         } else if (!g_ascii_strncasecmp (str.c_str (), X_ ("-inf"), str.length ()) ||
157                    !g_ascii_strncasecmp (str.c_str (), X_ ("-INFINITY"), str.length ())) {
158                 val = -std::numeric_limits<FloatType>::infinity ();
159                 return true;
160         }
161         return false;
162 }
163
164 bool
165 _string_to_double (const std::string& str, double& val)
166 {
167         val = g_ascii_strtod (str.c_str (), NULL);
168
169         // It is possible that the conversion was successful and another thread
170         // has set errno meanwhile but as most conversions are currently not
171         // checked for error conditions this is better than nothing.
172         if (errno == ERANGE) {
173                 DEBUG_SCONVERT (string_compose ("string_to_double possible conversion failure for %1", str));
174                 // There should not be any conversion failures as we control the string
175                 // contents so returning false here should not have any impact...
176                 return false;
177         }
178         return true;
179 }
180
181 bool string_to_float (const std::string& str, float& val)
182 {
183         double tmp;
184         if (_string_to_double (str, tmp)) {
185                 val = (float)tmp;
186                 return true;
187         }
188
189         if (_string_to_infinity (str, val)) {
190                 return true;
191         }
192
193         return false;
194 }
195
196 bool string_to_double (const std::string& str, double& val)
197 {
198         if (_string_to_double (str, val)) {
199                 return true;
200         }
201
202         if (_string_to_infinity (str, val)) {
203                 return true;
204         }
205
206         return false;
207 }
208
209 bool bool_to_string (bool val, std::string& str)
210 {
211         if (val) {
212                 str = X_("1");
213         } else {
214                 str = X_("0");
215         }
216         return true;
217 }
218
219 bool int16_to_string (int16_t val, std::string& str)
220 {
221         char buffer[CONVERT_BUF_SIZE];
222
223         int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIi16, val);
224
225         if (retval <= 0 || retval >= (int)sizeof(buffer)) {
226                 DEBUG_SCONVERT (
227                     string_compose ("int16_to_string conversion failure for %1", val));
228                 return false;
229         }
230         str = buffer;
231         return true;
232 }
233
234 bool uint16_to_string (uint16_t val, std::string& str)
235 {
236         char buffer[CONVERT_BUF_SIZE];
237
238         int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIu16, val);
239
240         if (retval <= 0 || retval >= (int)sizeof(buffer)) {
241                 DEBUG_SCONVERT (
242                     string_compose ("uint16_to_string conversion failure for %1", val));
243                 return false;
244         }
245         str = buffer;
246         return true;
247 }
248
249 bool int32_to_string (int32_t val, std::string& str)
250 {
251         char buffer[CONVERT_BUF_SIZE];
252
253         int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIi32, val);
254
255         if (retval <= 0 || retval >= (int)sizeof(buffer)) {
256                 DEBUG_SCONVERT (
257                     string_compose ("int32_to_string conversion failure for %1", val));
258                 return false;
259         }
260         str = buffer;
261         return true;
262 }
263
264 bool uint32_to_string (uint32_t val, std::string& str)
265 {
266         char buffer[CONVERT_BUF_SIZE];
267
268         int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIu32, val);
269
270         if (retval <= 0 || retval >= (int)sizeof(buffer)) {
271                 DEBUG_SCONVERT (
272                     string_compose ("uint32_to_string conversion failure for %1", val));
273                 return false;
274         }
275         str = buffer;
276         return true;
277 }
278
279 bool int64_to_string (int64_t val, std::string& str)
280 {
281         char buffer[CONVERT_BUF_SIZE];
282
283         int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIi64, val);
284
285         if (retval <= 0 || retval >= (int)sizeof(buffer)) {
286                 DEBUG_SCONVERT (
287                     string_compose ("int64_to_string conversion failure for %1", val));
288                 return false;
289         }
290         str = buffer;
291         return true;
292 }
293
294 bool uint64_to_string (uint64_t val, std::string& str)
295 {
296         char buffer[CONVERT_BUF_SIZE];
297
298         int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIu64, val);
299
300         if (retval <= 0 || retval >= (int)sizeof(buffer)) {
301                 DEBUG_SCONVERT (
302                     string_compose ("uint64_to_string conversion failure for %1", val));
303                 return false;
304         }
305         str = buffer;
306         return true;
307 }
308
309 template <class FloatType>
310 static bool
311 _infinity_to_string (FloatType val, std::string& str)
312 {
313         if (val == std::numeric_limits<FloatType>::infinity ()) {
314                 str = "inf";
315                 return true;
316         } else if (val == -std::numeric_limits<FloatType>::infinity ()) {
317                 str = "-inf";
318                 return true;
319         }
320         return false;
321 }
322
323 static bool
324 _double_to_string (double val, std::string& str)
325 {
326         char buffer[G_ASCII_DTOSTR_BUF_SIZE];
327
328         char* d_cstr = g_ascii_dtostr (buffer, sizeof(buffer), val);
329
330         if (d_cstr == NULL) {
331                 return false;
332         }
333         str = d_cstr;
334         return true;
335 }
336
337 bool float_to_string (float val, std::string& str)
338 {
339         if (_infinity_to_string (val, str)) {
340                 return true;
341         }
342
343         if (_double_to_string (val, str)) {
344                 return true;
345         }
346
347         DEBUG_SCONVERT (string_compose ("float_to_string conversion failure for %1", val));
348         return false;
349 }
350
351 bool double_to_string (double val, std::string& str)
352 {
353         if (_infinity_to_string (val, str)) {
354                 return true;
355         }
356
357         if (_double_to_string (val, str)) {
358                 return true;
359         }
360
361         DEBUG_SCONVERT (string_compose ("double_to_string conversion failure for %1", val));
362         return false;
363 }
364
365 } // namespace PBD