]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/stringio.h
Merge branch 'NateEag-master-patch-12920' into 'master'
[xonotic/netradiant.git] / libs / stringio.h
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant 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
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #if !defined ( INCLUDED_STRINGIO_H )
23 #define INCLUDED_STRINGIO_H
24
25 #include <stdlib.h>
26 #include <cctype>
27
28 #include "generic/vector.h"
29 #include "iscriplib.h"
30 #include "string/string.h"
31 #include "generic/callback.h"
32 #include "property.h"
33
34 inline float string_read_float( const char* string ){
35         return static_cast<float>( atof( string ) );
36 }
37
38 inline int string_read_int( const char* string ){
39         return atoi( string );
40 }
41
42 inline bool char_is_whitespace( char c ){
43         return c == ' ' || c == '\t';
44 }
45
46 inline const char* string_remove_whitespace( const char* string ){
47         for (;; )
48         {
49                 if ( !char_is_whitespace( *string ) ) {
50                         break;
51                 }
52                 ++string;
53         }
54         return string;
55 }
56
57 inline const char* string_remove_zeros( const char* string ){
58         for (;; )
59         {
60                 char c = *string;
61                 if ( c != '0' ) {
62                         break;
63                 }
64                 ++string;
65         }
66         return string;
67 }
68
69 inline const char* string_remove_sign( const char* string ){
70         if ( *string == '-' || *string == '+' ) { // signed zero - acceptable
71                 return ++string;
72         }
73         return string;
74 }
75
76 inline bool string_is_unsigned_zero( const char* string ){
77         for (; *string != '\0'; ++string )
78         {
79                 if ( *string != '0' ) {
80                         return false;
81                 }
82         }
83         return true;
84 }
85
86 inline bool string_is_signed_zero( const char* string ){
87         return string_is_unsigned_zero( string_remove_sign( string ) );
88 }
89
90 //[whitespaces][+|-][nnnnn][.nnnnn][e|E[+|-]nnnn]
91 //(where whitespaces are any tab or space character and nnnnn may be any number of digits)
92 inline bool string_is_float_zero( const char* string ){
93         string = string_remove_whitespace( string );
94         if ( string_empty( string ) ) {
95                 return false;
96         }
97
98         string = string_remove_sign( string );
99         if ( string_empty( string ) ) {
100                 // no whole number or fraction part
101                 return false;
102         }
103
104         // whole-number part
105         string = string_remove_zeros( string );
106         if ( string_empty( string ) ) {
107                 // no fraction or exponent
108                 return true;
109         }
110         if ( *string == '.' ) {
111                 // fraction part
112                 if ( *string++ != '0' ) {
113                         // invalid fraction
114                         return false;
115                 }
116                 string = string_remove_zeros( ++string );
117                 if ( string_empty( string ) ) {
118                         // no exponent
119                         return true;
120                 }
121         }
122         if ( *string == 'e' || *string == 'E' ) {
123                 // exponent part
124                 string = string_remove_sign( ++string );
125                 if ( *string++ != '0' ) {
126                         // invalid exponent
127                         return false;
128                 }
129                 string = string_remove_zeros( ++string );
130                 if ( string_empty( string ) ) {
131                         // no trailing whitespace
132                         return true;
133                 }
134         }
135         string = string_remove_whitespace( string );
136         return string_empty( string );
137 }
138
139 inline double buffer_parse_floating_literal( const char*& buffer ){
140         return strtod( buffer, const_cast<char**>( &buffer ) );
141 }
142
143 inline int buffer_parse_signed_decimal_integer_literal( const char*& buffer ){
144         return strtol( buffer, const_cast<char**>( &buffer ), 10 );
145 }
146
147 inline int buffer_parse_unsigned_decimal_integer_literal( const char*& buffer ){
148         return strtoul( buffer, const_cast<char**>( &buffer ), 10 );
149 }
150
151 // [+|-][nnnnn][.nnnnn][e|E[+|-]nnnnn]
152 inline bool string_parse_float( const char* string, float& f ){
153         if ( string_empty( string ) ) {
154                 return false;
155         }
156         f = float(buffer_parse_floating_literal( string ) );
157         return string_empty( string );
158 }
159
160 // format same as float
161 inline bool string_parse_double( const char* string, double& f ){
162         if ( string_empty( string ) ) {
163                 return false;
164         }
165         f = buffer_parse_floating_literal( string );
166         return string_empty( string );
167 }
168
169 // <float><space><float><space><float>
170 template<typename Element>
171 inline bool string_parse_vector3( const char* string, BasicVector3<Element>& v ){
172         if ( string_empty( string ) || *string == ' ' ) {
173                 return false;
174         }
175         v[0] = float(buffer_parse_floating_literal( string ) );
176         if ( *string++ != ' ' ) {
177                 return false;
178         }
179         v[1] = float(buffer_parse_floating_literal( string ) );
180         if ( *string++ != ' ' ) {
181                 return false;
182         }
183         v[2] = float(buffer_parse_floating_literal( string ) );
184         return string_empty( string );
185 }
186
187 template<typename Float>
188 inline bool string_parse_vector( const char* string, Float* first, Float* last ){
189         if ( first != last && ( string_empty( string ) || *string == ' ' ) ) {
190                 return false;
191         }
192         for (;; )
193         {
194                 *first = float(buffer_parse_floating_literal( string ) );
195                 if ( ++first == last ) {
196                         return string_empty( string );
197                 }
198                 if ( *string++ != ' ' ) {
199                         return false;
200                 }
201         }
202 }
203
204 // decimal signed integer
205 inline bool string_parse_int( const char* string, int& i ){
206         if ( string_empty( string ) ) {
207                 return false;
208         }
209         i = buffer_parse_signed_decimal_integer_literal( string );
210         return string_empty( string );
211 }
212
213 // decimal unsigned integer
214 inline bool string_parse_size( const char* string, std::size_t& i ){
215         if ( string_empty( string ) ) {
216                 return false;
217         }
218         i = buffer_parse_unsigned_decimal_integer_literal( string );
219         return string_empty( string );
220 }
221
222
223 #define RETURN_FALSE_IF_FAIL( expression ) do{ if ( !expression ) {return false; } }while( 0 )
224
225 inline void Tokeniser_unexpectedError( Tokeniser& tokeniser, const char* token, const char* expected ){
226         globalErrorStream() << Unsigned( tokeniser.getLine() ) << ":" << Unsigned( tokeniser.getColumn() ) << ": parse error at '" << ( token != 0 ? token : "#EOF" ) << "': expected '" << expected << "'\n";
227 }
228
229
230 inline bool Tokeniser_getFloat( Tokeniser& tokeniser, float& f ){
231         const char* token = tokeniser.getToken();
232         if ( token != 0 && string_parse_float( token, f ) ) {
233                 return true;
234         }
235         //fallback for 1.#IND 1.#INF 1.#QNAN cases, happening sometimes after rotating & often scaling with tex lock in BP mode
236         else if ( token != 0 && strstr( token, ".#" ) ) {
237                 globalErrorStream() << "Warning: " << Unsigned( tokeniser.getLine() ) << ":" << Unsigned( tokeniser.getColumn() ) << ": expected parse problem at '" << token << "': wanted '#number'\nProcessing anyway\n";
238         #define GARUX_DISABLE_QNAN_FALLBACK
239         #ifndef GARUX_DISABLE_QNAN_FALLBACK
240 //              *strstr( token, ".#" ) = '\0';
241         #endif
242                 return true;
243         }
244         Tokeniser_unexpectedError( tokeniser, token, "#number" );
245         return false;
246 }
247
248 inline bool Tokeniser_getDouble( Tokeniser& tokeniser, double& f ){
249         const char* token = tokeniser.getToken();
250         if ( token != 0 && string_parse_double( token, f ) ) {
251                 return true;
252         }
253         Tokeniser_unexpectedError( tokeniser, token, "#number" );
254         return false;
255 }
256
257 inline bool Tokeniser_getInteger( Tokeniser& tokeniser, int& i ){
258         const char* token = tokeniser.getToken();
259         if ( token != 0 && string_parse_int( token, i ) ) {
260                 return true;
261         }
262         Tokeniser_unexpectedError( tokeniser, token, "#integer" );
263         return false;
264 }
265
266 inline bool Tokeniser_getSize( Tokeniser& tokeniser, std::size_t& i ){
267         const char* token = tokeniser.getToken();
268         if ( token != 0 && string_parse_size( token, i ) ) {
269                 return true;
270         }
271         Tokeniser_unexpectedError( tokeniser, token, "#unsigned-integer" );
272         return false;
273 }
274
275 inline bool Tokeniser_parseToken( Tokeniser& tokeniser, const char* expected ){
276         const char* token = tokeniser.getToken();
277         if ( token != 0 && string_equal( token, expected ) ) {
278                 return true;
279         }
280         Tokeniser_unexpectedError( tokeniser, token, expected );
281         return false;
282 }
283
284 inline bool Tokeniser_nextTokenIsDigit( Tokeniser& tokeniser ){
285         const char* token = tokeniser.getToken();
286         if ( token == 0 ) {
287                 return false;
288         }
289         char c = *token;
290         tokeniser.ungetToken();
291         return std::isdigit( c ) != 0;
292 }
293
294 template<typename TextOutputStreamType>
295 inline TextOutputStreamType& ostream_write( TextOutputStreamType& outputStream, const Vector3& v ){
296         return outputStream << '(' << v.x() << ' ' << v.y() << ' ' << v.z() << ')';
297 }
298
299
300 template<>
301 struct PropertyImpl<bool, const char *> {
302         static void Export(const bool &self, const Callback<void(const char *)> &returnz) {
303                 returnz(self ? "true" : "false");
304         }
305
306     static void Import(bool &self, const char *value) {
307         self = string_equal(value, "true");
308     }
309 };
310
311 template<>
312 struct PropertyImpl<int, const char *> {
313         static void Export(const int &self, const Callback<void(const char *)> &returnz) {
314                 char buffer[16];
315                 sprintf(buffer, "%d", self);
316                 returnz(buffer);
317         }
318
319     static void Import(int &self, const char *value) {
320         if (!string_parse_int(value, self)) {
321             self = 0;
322         }
323     }
324 };
325
326 template<>
327 struct PropertyImpl<std::size_t, const char *> {
328         static void Export(const std::size_t &self, const Callback<void(const char *)> &returnz) {
329                 char buffer[16];
330                 sprintf(buffer, "%u", Unsigned(self));
331                 returnz(buffer);
332         }
333
334     static void Import(std::size_t &self, const char *value) {
335         int i;
336         if (string_parse_int(value, i) && i >= 0) {
337             self = i;
338         } else {
339             self = 0;
340         }
341     }
342 };
343
344 template<>
345 struct PropertyImpl<float, const char *> {
346         static void Export(const float &self, const Callback<void(const char *)> &returnz) {
347                 char buffer[16];
348                 sprintf(buffer, "%g", self);
349                 returnz(buffer);
350         }
351
352     static void Import(float &self, const char *value) {
353         if (!string_parse_float(value, self)) {
354             self = 0;
355         }
356     }
357 };
358
359 template<>
360 struct PropertyImpl<Vector3, const char *> {
361         static void Export(const Vector3 &self, const Callback<void(const char *)> &returnz) {
362                 char buffer[64];
363                 sprintf(buffer, "%g %g %g", self[0], self[1], self[2]);
364                 returnz(buffer);
365         }
366
367     static void Import(Vector3 &self, const char *value) {
368         if (!string_parse_vector3(value, self)) {
369             self = Vector3(0, 0, 0);
370         }
371     }
372 };
373
374 #endif