caf93865a0dfba9349584b98a4e7e85d5fe42bf4
[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
33 inline float string_read_float( const char* string ){
34         return static_cast<float>( atof( string ) );
35 }
36
37 inline int string_read_int( const char* string ){
38         return atoi( string );
39 }
40
41 inline bool char_is_whitespace( char c ){
42         return c == ' ' || c == '\t';
43 }
44
45 inline const char* string_remove_whitespace( const char* string ){
46         for (;; )
47         {
48                 if ( !char_is_whitespace( *string ) ) {
49                         break;
50                 }
51                 ++string;
52         }
53         return string;
54 }
55
56 inline const char* string_remove_zeros( const char* string ){
57         for (;; )
58         {
59                 char c = *string;
60                 if ( c != '0' ) {
61                         break;
62                 }
63                 ++string;
64         }
65         return string;
66 }
67
68 inline const char* string_remove_sign( const char* string ){
69         if ( *string == '-' || *string == '+' ) { // signed zero - acceptable
70                 return ++string;
71         }
72         return string;
73 }
74
75 inline bool string_is_unsigned_zero( const char* string ){
76         for (; *string != '\0'; ++string )
77         {
78                 if ( *string != '0' ) {
79                         return false;
80                 }
81         }
82         return true;
83 }
84
85 inline bool string_is_signed_zero( const char* string ){
86         return string_is_unsigned_zero( string_remove_sign( string ) );
87 }
88
89 //[whitespaces][+|-][nnnnn][.nnnnn][e|E[+|-]nnnn]
90 //(where whitespaces are any tab or space character and nnnnn may be any number of digits)
91 inline bool string_is_float_zero( const char* string ){
92         string = string_remove_whitespace( string );
93         if ( string_empty( string ) ) {
94                 return false;
95         }
96
97         string = string_remove_sign( string );
98         if ( string_empty( string ) ) {
99                 // no whole number or fraction part
100                 return false;
101         }
102
103         // whole-number part
104         string = string_remove_zeros( string );
105         if ( string_empty( string ) ) {
106                 // no fraction or exponent
107                 return true;
108         }
109         if ( *string == '.' ) {
110                 // fraction part
111                 if ( *string++ != '0' ) {
112                         // invalid fraction
113                         return false;
114                 }
115                 string = string_remove_zeros( ++string );
116                 if ( string_empty( string ) ) {
117                         // no exponent
118                         return true;
119                 }
120         }
121         if ( *string == 'e' || *string == 'E' ) {
122                 // exponent part
123                 string = string_remove_sign( ++string );
124                 if ( *string++ != '0' ) {
125                         // invalid exponent
126                         return false;
127                 }
128                 string = string_remove_zeros( ++string );
129                 if ( string_empty( string ) ) {
130                         // no trailing whitespace
131                         return true;
132                 }
133         }
134         string = string_remove_whitespace( string );
135         return string_empty( string );
136 }
137
138 inline double buffer_parse_floating_literal( const char*& buffer ){
139         return strtod( buffer, const_cast<char**>( &buffer ) );
140 }
141
142 inline int buffer_parse_signed_decimal_integer_literal( const char*& buffer ){
143         return strtol( buffer, const_cast<char**>( &buffer ), 10 );
144 }
145
146 inline int buffer_parse_unsigned_decimal_integer_literal( const char*& buffer ){
147         return strtoul( buffer, const_cast<char**>( &buffer ), 10 );
148 }
149
150 // [+|-][nnnnn][.nnnnn][e|E[+|-]nnnnn]
151 inline bool string_parse_float( const char* string, float& f ){
152         if ( string_empty( string ) ) {
153                 return false;
154         }
155         f = float(buffer_parse_floating_literal( string ) );
156         return string_empty( string );
157 }
158
159 // format same as float
160 inline bool string_parse_double( const char* string, double& f ){
161         if ( string_empty( string ) ) {
162                 return false;
163         }
164         f = buffer_parse_floating_literal( string );
165         return string_empty( string );
166 }
167
168 // <float><space><float><space><float>
169 template<typename Element>
170 inline bool string_parse_vector3( const char* string, BasicVector3<Element>& v ){
171         if ( string_empty( string ) || *string == ' ' ) {
172                 return false;
173         }
174         v[0] = float(buffer_parse_floating_literal( string ) );
175         if ( *string++ != ' ' ) {
176                 return false;
177         }
178         v[1] = float(buffer_parse_floating_literal( string ) );
179         if ( *string++ != ' ' ) {
180                 return false;
181         }
182         v[2] = float(buffer_parse_floating_literal( string ) );
183         return string_empty( string );
184 }
185
186 template<typename Float>
187 inline bool string_parse_vector( const char* string, Float* first, Float* last ){
188         if ( first != last && ( string_empty( string ) || *string == ' ' ) ) {
189                 return false;
190         }
191         for (;; )
192         {
193                 *first = float(buffer_parse_floating_literal( string ) );
194                 if ( ++first == last ) {
195                         return string_empty( string );
196                 }
197                 if ( *string++ != ' ' ) {
198                         return false;
199                 }
200         }
201 }
202
203 // decimal signed integer
204 inline bool string_parse_int( const char* string, int& i ){
205         if ( string_empty( string ) ) {
206                 return false;
207         }
208         i = buffer_parse_signed_decimal_integer_literal( string );
209         return string_empty( string );
210 }
211
212 // decimal unsigned integer
213 inline bool string_parse_size( const char* string, std::size_t& i ){
214         if ( string_empty( string ) ) {
215                 return false;
216         }
217         i = buffer_parse_unsigned_decimal_integer_literal( string );
218         return string_empty( string );
219 }
220
221
222 #define RETURN_FALSE_IF_FAIL(expression) do { if (!(expression)) return false; } while (0)
223
224 inline void Tokeniser_unexpectedError( Tokeniser& tokeniser, const char* token, const char* expected ){
225         globalErrorStream() << Unsigned( tokeniser.getLine() ) << ":" << Unsigned( tokeniser.getColumn() ) << ": parse error at '" << ( token != 0 ? token : "#EOF" ) << "': expected '" << expected << "'\n";
226 }
227
228
229 inline bool Tokeniser_getFloat( Tokeniser& tokeniser, float& f ){
230         const char* token = tokeniser.getToken();
231         if ( token != 0 && string_parse_float( token, f ) ) {
232                 return true;
233         }
234         Tokeniser_unexpectedError( tokeniser, token, "#number" );
235         return false;
236 }
237
238 inline bool Tokeniser_getDouble( Tokeniser& tokeniser, double& f ){
239         const char* token = tokeniser.getToken();
240         if ( token != 0 && string_parse_double( token, f ) ) {
241                 return true;
242         }
243         Tokeniser_unexpectedError( tokeniser, token, "#number" );
244         return false;
245 }
246
247 inline bool Tokeniser_getInteger( Tokeniser& tokeniser, int& i ){
248         const char* token = tokeniser.getToken();
249         if ( token != 0 && string_parse_int( token, i ) ) {
250                 return true;
251         }
252         Tokeniser_unexpectedError( tokeniser, token, "#integer" );
253         return false;
254 }
255
256 inline bool Tokeniser_getSize( Tokeniser& tokeniser, std::size_t& i ){
257         const char* token = tokeniser.getToken();
258         if ( token != 0 && string_parse_size( token, i ) ) {
259                 return true;
260         }
261         Tokeniser_unexpectedError( tokeniser, token, "#unsigned-integer" );
262         return false;
263 }
264
265 inline bool Tokeniser_parseToken( Tokeniser& tokeniser, const char* expected ){
266         const char* token = tokeniser.getToken();
267         if ( token != 0 && string_equal( token, expected ) ) {
268                 return true;
269         }
270         Tokeniser_unexpectedError( tokeniser, token, expected );
271         return false;
272 }
273
274 inline bool Tokeniser_nextTokenIsDigit( Tokeniser& tokeniser ){
275         const char* token = tokeniser.getToken();
276         if ( token == 0 ) {
277                 return false;
278         }
279         char c = *token;
280         tokeniser.ungetToken();
281         return std::isdigit( c ) != 0;
282 }
283
284 template<typename TextOutputStreamType>
285 inline TextOutputStreamType& ostream_write( TextOutputStreamType& outputStream, const Vector3& v ){
286         return outputStream << '(' << v.x() << ' ' << v.y() << ' ' << v.z() << ')';
287 }
288
289
290
291
292 inline void CopiedString_importString( CopiedString& self, const char* string ){
293         self = string;
294 }
295 typedef ReferenceCaller<CopiedString, void(const char*), CopiedString_importString> CopiedStringImportStringCaller;
296 inline void CopiedString_exportString( const CopiedString& self, const StringImportCallback& importer ){
297         importer( self.c_str() );
298 }
299 typedef ConstReferenceCaller<CopiedString, void(const StringImportCallback&), CopiedString_exportString> CopiedStringExportStringCaller;
300
301 inline void Bool_importString( bool& self, const char* string ){
302         self = string_equal( string, "true" );
303 }
304 typedef ReferenceCaller<bool, void(const char*), Bool_importString> BoolImportStringCaller;
305 inline void Bool_exportString( const bool& self, const StringImportCallback& importer ){
306         importer( self ? "true" : "false" );
307 }
308 typedef ConstReferenceCaller<bool, void(const StringImportCallback&), Bool_exportString> BoolExportStringCaller;
309
310 inline void Int_importString( int& self, const char* string ){
311         if ( !string_parse_int( string, self ) ) {
312                 self = 0;
313         }
314 }
315 typedef ReferenceCaller<int, void(const char*), Int_importString> IntImportStringCaller;
316 inline void Int_exportString( const int& self, const StringImportCallback& importer ){
317         char buffer[16];
318         sprintf( buffer, "%d", self );
319         importer( buffer );
320 }
321 typedef ConstReferenceCaller<int, void(const StringImportCallback&), Int_exportString> IntExportStringCaller;
322
323 inline void Size_importString( std::size_t& self, const char* string ){
324         int i;
325         if ( string_parse_int( string, i ) && i >= 0 ) {
326                 self = i;
327         }
328         else
329         {
330                 self = 0;
331         }
332 }
333 typedef ReferenceCaller<std::size_t, void(const char*), Size_importString> SizeImportStringCaller;
334 inline void Size_exportString( const std::size_t& self, const StringImportCallback& importer ){
335         char buffer[16];
336         sprintf( buffer, "%u", Unsigned( self ) );
337         importer( buffer );
338 }
339 typedef ConstReferenceCaller<std::size_t, void(const StringImportCallback&), Size_exportString> SizeExportStringCaller;
340
341 inline void Float_importString( float& self, const char* string ){
342         if ( !string_parse_float( string, self ) ) {
343                 self = 0;
344         }
345 }
346 typedef ReferenceCaller<float, void(const char*), Float_importString> FloatImportStringCaller;
347 inline void Float_exportString( const float& self, const StringImportCallback& importer ){
348         char buffer[16];
349         sprintf( buffer, "%g", self );
350         importer( buffer );
351 }
352 typedef ConstReferenceCaller<float, void(const StringImportCallback&), Float_exportString> FloatExportStringCaller;
353
354 inline void Vector3_importString( Vector3& self, const char* string ){
355         if ( !string_parse_vector3( string, self ) ) {
356                 self = Vector3( 0, 0, 0 );
357         }
358 }
359 typedef ReferenceCaller<Vector3, void(const char*), Vector3_importString> Vector3ImportStringCaller;
360 inline void Vector3_exportString( const Vector3& self, const StringImportCallback& importer ){
361         char buffer[64];
362         sprintf( buffer, "%g %g %g", self[0], self[1], self[2] );
363         importer( buffer );
364 }
365 typedef ConstReferenceCaller<Vector3, void(const StringImportCallback&), Vector3_exportString> Vector3ExportStringCaller;
366
367
368
369 template<typename FirstArgument, typename Caller, typename FirstConversion>
370 class ImportConvert1
371 {
372 public:
373 static void thunk( void* environment, FirstArgument firstArgument ){
374         Caller::thunk( environment, FirstConversion( firstArgument ) );
375 }
376 };
377
378
379 class BoolFromString
380 {
381 bool m_value;
382 public:
383 BoolFromString( const char* string ){
384         Bool_importString( m_value, string );
385 }
386 operator bool() const
387 {
388         return m_value;
389 }
390 };
391
392 inline void Bool_toString( const StringImportCallback& self, bool value ){
393         Bool_exportString( value, self );
394 }
395 typedef ConstReferenceCaller<StringImportCallback, void(bool), Bool_toString> BoolToString;
396
397
398 template<typename Caller>
399 inline StringImportCallback makeBoolStringImportCallback( const Caller& caller ){
400         return StringImportCallback( caller.getEnvironment(), ImportConvert1<get_argument<StringImportCallback, 0>, Caller, BoolFromString>::thunk );
401 }
402
403 template<typename Caller>
404 inline StringExportCallback makeBoolStringExportCallback( const Caller& caller ){
405         return StringExportCallback( caller.getEnvironment(), ImportConvert1<get_argument<StringExportCallback, 0>, Caller, BoolToString>::thunk );
406 }
407
408
409 class IntFromString
410 {
411 int m_value;
412 public:
413 IntFromString( const char* string ){
414         Int_importString( m_value, string );
415 }
416 operator int() const
417 {
418         return m_value;
419 }
420 };
421
422 inline void Int_toString( const StringImportCallback& self, int value ){
423         Int_exportString( value, self );
424 }
425 typedef ConstReferenceCaller<StringImportCallback, void(int), Int_toString> IntToString;
426
427
428 template<typename Caller>
429 inline StringImportCallback makeIntStringImportCallback( const Caller& caller ){
430         return StringImportCallback( caller.getEnvironment(), ImportConvert1<get_argument<StringImportCallback, 0>, Caller, IntFromString>::thunk );
431 }
432
433 template<typename Caller>
434 inline StringExportCallback makeIntStringExportCallback( const Caller& caller ){
435         return StringExportCallback( caller.getEnvironment(), ImportConvert1<get_argument<StringExportCallback, 0>, Caller, IntToString>::thunk );
436 }
437
438
439
440 class SizeFromString
441 {
442 std::size_t m_value;
443 public:
444 SizeFromString( const char* string ){
445         Size_importString( m_value, string );
446 }
447 operator std::size_t() const
448 {
449         return m_value;
450 }
451 };
452
453 inline void Size_toString( const StringImportCallback& self, std::size_t value ){
454         Size_exportString( value, self );
455 }
456 typedef ConstReferenceCaller<StringImportCallback, void(std::size_t), Size_toString> SizeToString;
457
458
459 template<typename Caller>
460 inline StringImportCallback makeSizeStringImportCallback( const Caller& caller ){
461         return StringImportCallback( caller.getEnvironment(), ImportConvert1<get_argument<StringImportCallback, 0>, Caller, SizeFromString>::thunk );
462 }
463
464 template<typename Caller>
465 inline StringExportCallback makeSizeStringExportCallback( const Caller& caller ){
466         return StringExportCallback( caller.getEnvironment(), ImportConvert1<get_argument<StringExportCallback, 0>, Caller, SizeToString>::thunk );
467 }
468
469 #endif