1 /*
2  * Copyright (C) 2000-2005 Chris Ross and various contributors
3  * Copyright (C) 1999-2000 Chris Ross
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * o Redistributions of source code must retain the above copyright notice, this
10  *   list of conditions and the following disclaimer.
11  * o Redistributions in binary form must reproduce the above copyright notice,
12  *   this list of conditions and the following disclaimer in the documentation
13  *   and/or other materials provided with the distribution.
14  * o Neither the name of the ferite software nor the names of its contributors may
15  *   be used to endorse or promote products derived from this software without
16  *   specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #ifdef HAVE_CONFIG_HEADER
32 #include "../config.h"
33 #endif
34 
35 #include "ferite.h"
36 
37 #if defined(WIN32) && !defined(USING_MINGW)
38 # include "snprintf.h" /* This is so that we have somethings */
39 #endif
40 
41 /**
42  * @group Error System
43  * @description There are a number of things a lot of languages lack and that is error handling.
44  *              The functions in this group allow for the raising of exceptions during runtime,
45  *              and compile time.
46  */
47 
48 /**
49  * @function ferite_raise_script_error
50  * @declaration void ferite_raise_script_error( FeriteScript *script, int err, char *fmt, ... )
51  * @brief Raise an exception within the ferite engine.
52  * @param FeriteScript *script The running script
53  * @param int err    The error code
54  * @param char *fmt    The format of the error string
55  * @description Use the same formating codes as printf with this function
56  */
ferite_raise_script_error(FeriteScript * script,int err,char * fmt,...)57 void ferite_raise_script_error( FeriteScript *script, int err, char *fmt, ... )
58 {
59     FeriteNamespaceBucket *nsb;
60     FeriteVariable *gerr, *newError;
61     FeriteVariable *errstr, *erno;
62     char *msg;
63     va_list ap;
64 
65     FE_ENTER_FUNCTION;
66     msg = fmalloc( 4096 );
67     va_start( ap, fmt );
68     vsprintf( msg, fmt, ap );
69     FUD(("ERROR RAISED: %s %d\n", msg, err ));
70 
71     nsb = ferite_namespace_element_exists( script, script->mainns, "err" );
72     FE_ASSERT( nsb && nsb->type == FENS_VAR );
73     gerr = nsb->data;
74     script->error_state = FE_ERROR_THROWN;
75 
76     if( VAO(gerr) == NULL )
77     {
78         nsb = ferite_namespace_element_exists( script, script->mainns, "Error" );
79         if( nsb == NULL )
80         {
81             FE_LEAVE_FUNCTION( NOWT );
82             exit(1);
83         }
84         newError = ferite_new_object( script, nsb->data, NULL );
85         VAO(gerr) = VAO(newError);
86         VAO(gerr)->refcount++;
87         ferite_variable_destroy( script, newError );
88     }
89 
90     errstr = ferite_object_get_var( script, VAO(gerr), "str" );
91     ferite_str_set( VAS(errstr), msg, strlen(msg), FE_CHARSET_DEFAULT );
92     ffree( msg );
93 
94     erno = ferite_object_get_var( script, VAO(gerr), "num" );
95     VAI(erno) = err;
96     FE_LEAVE_FUNCTION( NOWT );
97 }
98 
99 /**
100  * @function ferite_verror
101  * @declaration void ferite_verror( FeriteScript *script, char *errormsg, va_list *ap )
102  * @brief Raise an error
103  * @param FeriteScript *script The script
104  * @param int err The error number associated with the error
105  * @param char *errormsg The error with formating codes in it
106  * @param va_list *ap The list of arguments
107  */
ferite_verror(FeriteScript * script,int err,char * errormsg,va_list * ap)108 void ferite_verror( FeriteScript *script, int err, char *errormsg, va_list *ap )
109 {
110     char msg[1024];
111 
112     FE_ENTER_FUNCTION;
113 
114     if( script == NULL )
115     {
116         vprintf(errormsg, *ap );
117         FE_LEAVE_FUNCTION( NOWT );
118     }
119     if( script->error == NULL )
120       script->error = ferite_buffer_new( 0 );
121 
122     ferite_buffer_add_str( script->error, "Error: " );
123 
124     /* if( ferite_is_executing( script ) )
125         ferite_buffer_printf( script->error, "[%s:%d] ", script->current_op_file, script->current_op_line ); */
126     ferite_buffer_vprintf( script->error, errormsg, ap );
127     if( script->error_state != FE_ERROR_THROWN )
128 	{
129 		if( ferite_is_executing( script ) )
130 		{
131 			vsnprintf( msg, 1024, errormsg, *ap );
132 			ferite_raise_script_error( script, err, msg );
133 		}
134 		script->error_state = FE_ERROR_THROWN;
135 	}
136 
137     FE_LEAVE_FUNCTION( NOWT );
138 }
139 /**
140  * @function ferite_error
141  * @declaration void ferite_error( FeriteScript *script, char *errormsg, ... )
142  * @brief Throw an error within the engine
143  * @param FeriteScript *script   The scipt we are using
144  * @param int err The error number associated with the error
145  * @param char *errormsg The error information
146  * @param . .. The formating values
147  */
ferite_error(FeriteScript * script,int err,char * errormsg,...)148 void ferite_error( FeriteScript *script, int err, char *errormsg, ... )
149 {
150     va_list ap;
151 
152     FE_ENTER_FUNCTION;
153     va_start( ap, errormsg );
154     ferite_verror( script, err, errormsg, &ap);
155     va_end( ap );
156     FE_LEAVE_FUNCTION( NOWT );
157 }
158 
159 /**
160  * @function ferite_vwarning
161  * @declaration void ferite_vwarning( FeriteScript *script, char *errormsg, ... )
162  * @brief Display a warning message. This does not cause an exception
163  * @param FeriteScript *script   The current script
164  * @param char *errormsg The warning to be displayed
165  * @param va_list *ap The formatting values
166  */
ferite_vwarning(FeriteScript * script,char * errormsg,va_list * ap)167 void ferite_vwarning( FeriteScript *script, char *errormsg, va_list *ap )
168 {
169 
170     FE_ENTER_FUNCTION;
171 
172     if( script == NULL )
173     {
174         printf("ferite_warning(): script was called with NULL, this shouldn't happen\n");
175         vprintf(errormsg, *ap );
176 #ifdef DEBUG
177         printf("ferite_warning(): sleeping for gdb interruption for 10 seconds\n");
178         sleep(10);
179 #endif
180         FE_LEAVE_FUNCTION( NOWT );
181     }
182 
183     if( script->warning == NULL )
184       script->warning = ferite_buffer_new( 0 );
185 
186     ferite_buffer_add_str( script->warning, "Warning: " );
187     if( ferite_is_executing( script ) )
188       ferite_buffer_printf( script->warning, "[%s:%d] ", script->current_op_file, script->current_op_line );
189     ferite_buffer_vprintf( script->warning, errormsg, ap );
190 
191     FE_LEAVE_FUNCTION( NOWT );
192 }
193 
194 /**
195  * @function ferite_warning
196  * @declaration void ferite_warning( FeriteScript *script, char *errormsg, ... )
197  * @brief Display a warning message. This does not cause an exception
198  * @param FeriteScript *script   The current script
199  * @param char *errormsg The warning to be displayed
200  * @param . .. The formatting values
201  */
ferite_warning(FeriteScript * script,char * errormsg,...)202 void ferite_warning( FeriteScript *script, char *errormsg, ... )
203 {
204     va_list ap;
205 
206     FE_ENTER_FUNCTION;
207     va_start( ap, errormsg );
208 
209     ferite_vwarning( script, errormsg, &ap );
210 
211     va_end( ap );
212     FE_LEAVE_FUNCTION(NOWT);
213 }
214 
215 /**
216  * @function ferite_get_error_log
217  * @declaration char *ferite_get_error_log( FeriteScript *script )
218  * @brief Get a null terminated string containing the error and warning logs on a script
219  * @param FeriteScript *script The script to get the errror logs from
220  * @return A null terminated string, you will need to ffree the string when done to prevent memory leak
221  */
ferite_get_error_log(FeriteScript * script)222 char *ferite_get_error_log( FeriteScript *script )
223 {
224     int err_size = 0, warn_size = 0;
225     char *msg, *err_ptr, *warn_ptr;
226 
227     FE_ENTER_FUNCTION;
228     if( script->error )
229       err_ptr = ferite_buffer_get( script->error, &err_size );
230     else
231       err_ptr = fstrdup("");
232     if( script->warning )
233       warn_ptr = ferite_buffer_get( script->warning, &warn_size );
234     else
235       warn_ptr = fstrdup("");
236     msg = fmalloc( err_size + warn_size + 1 );
237     strcpy( msg, warn_ptr );
238     strcat( msg, err_ptr );
239     ffree( err_ptr );
240     ffree( warn_ptr );
241     FE_LEAVE_FUNCTION( msg );
242 }
243 
244 /**
245  * @function ferite_get_error_string
246  * @declaration char *ferite_get_error_string( FeriteScript *script )
247  * @brief Get a null terminated string containing the error log
248  * @param FeriteScript *script The script whose errors are required
249  * @return  A null terminated string, you will need to ffree the string when done to prevent memory leak
250  */
ferite_get_error_string(FeriteScript * script)251 char *ferite_get_error_string( FeriteScript *script )
252 {
253     char *msg;
254     FE_ENTER_FUNCTION;
255     if( script->error )
256       msg = ferite_buffer_get( script->error, NULL );
257     else
258       msg = fstrdup("");
259     FE_LEAVE_FUNCTION( msg );
260 }
261 
262 /**
263  * @function ferite_get_warning_string
264  * @declaration char *ferite_get_warning_string( FeriteScript *script )
265  * @brief Get a null terminated string containing the warning log
266  * @param FeriteScript *script The script whose warnings are required
267  * @return  A null terminated string, you will need to ffree the string when done to prevent memory leak
268  */
ferite_get_warning_string(FeriteScript * script)269 char *ferite_get_warning_string( FeriteScript *script )
270 {
271     char *msg;
272     FE_ENTER_FUNCTION;
273     if( script->warning )
274       msg = ferite_buffer_get( script->warning, NULL );
275     else
276       msg = fstrdup("");
277     FE_LEAVE_FUNCTION( msg );
278 }
279 
280 /**
281  * @function ferite_reset_warnings
282  * @declaration void ferite_reset_warnings( FeriteScript *script )
283  * @brief Reset any warnings on the script
284  * @param FeriteScript *script The script to check
285  */
ferite_reset_warnings(FeriteScript * script)286 void ferite_reset_warnings( FeriteScript *script )
287 {
288     FE_ENTER_FUNCTION;
289     if( script->warning != NULL )
290     {
291 	ferite_buffer_delete( script->warning );
292 	script->warning = NULL;
293     }
294     FE_LEAVE_FUNCTION(NOWT);
295 }
296 
297 /**
298  * @function ferite_reset_errors
299  * @declaration void ferite_reset_errors( FeriteScript *script )
300  * @brief Reset any errors on the script
301  * @param FeriteScript *script The script to check
302  */
ferite_reset_errors(FeriteScript * script)303 void ferite_reset_errors( FeriteScript *script )
304 {
305     FE_ENTER_FUNCTION;
306     if( script->error != NULL )
307     {
308 		ferite_buffer_delete( script->error );
309 		script->error = NULL;
310 		script->error_state = 0;
311     }
312     FE_LEAVE_FUNCTION(NOWT);
313 }
314 
315 /**
316  * @function ferite_has_compile_error
317  * @declaration int ferite_has_compile_error( FeriteScript *script )
318  * @brief Check to see if the script has had a compilation error
319  * @param FeriteScript *script The script to check
320  * @return FE_TRUE if a compilation error occured, FE_FALSE otherwise
321  */
ferite_has_compile_error(FeriteScript * script)322 int ferite_has_compile_error( FeriteScript *script )
323 {
324     FE_ENTER_FUNCTION;
325     FE_LEAVE_FUNCTION( ( script->error == NULL ) ? FE_FALSE : FE_TRUE );
326 }
327 
328 /**
329  * @function ferite_has_warnings
330  * @declaration int ferite_has_warnings( FeriteScript *script )
331  * @brief Check to see if the script has warnings
332  * @param FeriteScript *script The script to check
333  * @return FE_TRUE if there are warnings, FE_FALSE otherwise
334  */
ferite_has_warnings(FeriteScript * script)335 int ferite_has_warnings( FeriteScript *script )
336 {
337     FE_ENTER_FUNCTION;
338     FE_LEAVE_FUNCTION( (script->warning == NULL) ? FE_FALSE : FE_TRUE );
339 }
340 
341 /**
342  * @function ferite_has_runtime_error
343  * @declaration int ferite_has_runtime_error( FeriteScript *script )
344  * @brief Check to see if the script has had a runtime error
345  * @param FeriteScript *script The script to check
346  * @return FE_TRUE if a compilation error occured, FE_FALSE otherwise
347  */
ferite_has_runtime_error(FeriteScript * script)348 int ferite_has_runtime_error( FeriteScript *script )
349 {
350     FE_ENTER_FUNCTION;
351     FE_LEAVE_FUNCTION( ( script->error_state == FE_ERROR_THROWN ) ? FE_TRUE : FE_FALSE );
352 }
353 
354 /**
355  * @function ferite_init_error_system
356  * @declaration void ferite_init_error_system( FeriteScript *script, FeriteNamespace *ns )
357  * @brief Setup the special error handling class on a script
358  * @param FeriteScript *script The current script
359  * @param FeriteNamespace *ns     The namespace in which the class should be registered
360  */
ferite_init_error_system(FeriteScript * script,FeriteNamespace * ns)361 void ferite_init_error_system( FeriteScript *script, FeriteNamespace *ns )
362 {
363     FeriteClass *ferite_error_class = NULL;
364     FeriteVariable *var, *errobj = NULL;
365     FeriteNamespaceBucket *nsb = NULL;
366 
367     FE_ENTER_FUNCTION;
368     ferite_error_class = ferite_register_class( script, ns, "Error" );
369     ferite_register_class_variable( script, ferite_error_class, ferite_create_number_long_variable( script, "num", 0, FE_STATIC ), 0 );
370     ferite_register_class_variable( script, ferite_error_class, ferite_create_string_variable( script, "str", NULL, FE_STATIC ), 0 );
371 
372     nsb = ferite_find_namespace( script, script->mainns, "err", FENS_VAR );
373     if( nsb != NULL )
374     {
375 	errobj = ferite_build_object( script, ferite_error_class );
376 	var = nsb->data;
377 	VAO(var) = VAO(errobj);
378 	VAO(errobj) = NULL;
379 	ferite_variable_destroy( script, errobj );
380     }
381 
382     FE_LEAVE_FUNCTION(NOWT);
383 }
384 
385 /**
386  * @function ferite_set_error
387  * @declaration void ferite_set_error( FeriteScript *script, int num, char *fmt, ... )
388  * @brief Same as ferite_error except this wont raise an exception at runtime
389  */
ferite_set_error(FeriteScript * script,int num,char * fmt,...)390 void ferite_set_error( FeriteScript *script, int num, char *fmt, ... )
391 {
392     FeriteNamespaceBucket *nsb = NULL;
393     FeriteVariable *gerr = NULL, *newError = NULL;
394     FeriteVariable *errstr = NULL, *erno = NULL;
395     va_list ap;
396     char *buf = NULL;
397 
398     FE_ENTER_FUNCTION;
399 
400     if( !script->is_being_deleted && (script->parent == NULL || !script->parent->is_being_deleted) )
401     {
402         buf = fmalloc( 4096 );
403         va_start( ap, fmt );
404         vsprintf( buf, fmt, ap );
405 
406         nsb = ferite_namespace_element_exists( script, script->mainns, "err" );
407         FE_ASSERT( nsb && nsb->type == FENS_VAR );
408         gerr = nsb->data;
409 
410         if( VAO(gerr) == NULL )
411         {
412             nsb = ferite_namespace_element_exists( script, script->mainns, "Error" );
413             newError = ferite_new_object( script, nsb->data, NULL );
414             VAO(gerr) = VAO(newError);
415             VAO(gerr)->refcount++;
416             ferite_variable_destroy( script, newError );
417         }
418 
419         errstr = ferite_object_get_var( script, VAO(gerr), "str" );
420         ferite_str_set( VAS(errstr), buf, strlen(buf), FE_CHARSET_DEFAULT );
421 
422         erno = ferite_object_get_var( script, VAO(gerr), "num" );
423         VAI(erno) = num;
424 
425         ffree( buf );
426         va_end( ap );
427     }
428     FE_LEAVE_FUNCTION( NOWT );
429 }
430 /* This function will never return, set ferite_assert_debug to generate
431  * a segfault to get a backtrace */
432 int ferite_assert_debug = 0;
ferite_assert(char * fmt,...)433 void ferite_assert( char *fmt, ... )
434 {
435     char *p = NULL;
436     va_list ap;
437     va_start( ap, fmt );
438     ferite_vwarning( NULL, fmt, &ap );
439     va_end( ap );
440     if( ferite_assert_debug )
441       *p = '\0';
442     exit( -1 );
443 }
444 
445 /**
446  * @end
447  */
448