00001 #include <stdio.h> 00002 #include <ruby.h> 00003 #include <alloca.h> 00004 #include "moment_parser.h" 00005 #include "interval_parser.h" 00006 00007 #define Check_Time(t) \ 00008 if (!rb_obj_is_instance_of(t, rb_cTime)) { \ 00009 rb_raise(rb_eTypeError, "%s not a Time", #t); \ 00010 } 00011 00012 /* 00013 gboolean parse_moment(const char* s, time_t ctx, time_t now, 00014 time_t* result, GError** error) 00015 gboolean parse_interval(const char* s, time_t ctx, time_t now, 00016 time_t* beg, time_t* end, GError** error) 00017 */ 00018 00019 // Returns a result Time Range or :always or nil. Throws an 00020 // exception on a parse error. 00021 static VALUE rb_parse_interval(VALUE self, VALUE s, 00022 VALUE ctx, VALUE now) 00023 { 00024 Check_Time(ctx); 00025 Check_Time(now); 00026 00027 char *c_str = StringValuePtr(s); 00028 time_t c_ctx = NUM2LONG(rb_funcall(ctx, rb_intern("to_i"), 0)); 00029 time_t c_now = NUM2LONG(rb_funcall(now, rb_intern("to_i"), 0)); 00030 time_t c_result_b = 0; 00031 time_t c_result_e = 0; 00032 GError* error = NULL; 00033 if (!parse_interval(c_str, c_ctx, c_now, 00034 &c_result_b, &c_result_e, &error)) { 00035 char* msg = alloca(strlen(error->message) + 1); 00036 strcpy(msg, error->message); 00037 g_error_free(error); 00038 rb_raise(rb_eSyntaxError, msg); 00039 } 00040 00041 if (!c_result_b && !c_result_e) { 00042 return Qnil; 00043 } else if (!c_result_e) { 00044 return ID2SYM(rb_intern("always")); 00045 } else { 00046 // We probably have to do GC registrations here. Suppose, when 00047 // allocating the second time object, the first one is GCd as the 00048 // runtime is not aware of any references to it. 00049 VALUE tms = rb_funcall(rb_cTime, rb_intern("at"), 1, LONG2NUM(c_result_b)); 00050 rb_gc_register_address(&tms); 00051 VALUE tme = rb_funcall(rb_cTime, rb_intern("at"), 1, LONG2NUM(c_result_e)); 00052 rb_gc_register_address(&tme); 00053 VALUE range = rb_funcall(rb_cRange, rb_intern("new"), 2, tms, tme); 00054 rb_gc_unregister_address(&tms); 00055 rb_gc_unregister_address(&tme); 00056 return range; 00057 } 00058 } 00059 00060 // Returns the result Time or nil (indicating no such moment 00061 // upcoming). Throws an exception on a parse error. 00062 static VALUE rb_parse_moment(VALUE self, VALUE s, 00063 VALUE ctx, VALUE now) 00064 { 00065 Check_Time(ctx); 00066 Check_Time(now); 00067 00068 char *c_str = StringValuePtr(s); 00069 time_t c_ctx = NUM2LONG(rb_funcall(ctx, rb_intern("to_i"), 0)); 00070 time_t c_now = NUM2LONG(rb_funcall(now, rb_intern("to_i"), 0)); 00071 time_t c_result = 0; 00072 GError* error = NULL; 00073 if (!parse_moment(c_str, c_ctx, c_now, &c_result, &error)) { 00074 // A problem here is that we must pass a string to rb_raise, one 00075 // which really has to be freed at some point, but rb_raise does 00076 // not give us a chance to do cleanup before causing a return from 00077 // this function. Also, wrapping the string inside a Ruby object 00078 // does not really work either, since Ruby would not know about 00079 // there being a reference to the string, and hence might GC it 00080 // too early, unless we used rb_gc_register_address, but if we 00081 // used that we would also have to later use 00082 // rb_gc_unregister_address, and there we are back to square one. 00083 // The only solution is to copy the string to the stack, either 00084 // using a fixed size buffer or "alloca". 00085 // 00086 // Another alternative would be to construct the exception 00087 // instance first, and only then throw it. 00088 char* msg = alloca(strlen(error->message) + 1); 00089 strcpy(msg, error->message); 00090 g_error_free(error); 00091 rb_raise(rb_eSyntaxError, msg); 00092 } 00093 00094 if (c_result == 0) 00095 return Qnil; 00096 else 00097 // This should be okay. The "at" method will have the number 00098 // object on its stack, and hence we can trust the number not to 00099 // get GCd too early. Besides, the number probably is not even a 00100 // real object, but rather just a labeled value. 00101 return rb_funcall(rb_cTime, rb_intern("at"), 1, LONG2NUM(c_result)); 00102 } 00103 00104 void Init_time_parser_rb() 00105 { 00106 VALUE topModule = rb_define_module("TimeParser"); 00107 rb_define_module_function(topModule, "parse_moment", rb_parse_moment, 3); 00108 rb_define_module_function(topModule, "parse_interval", rb_parse_interval, 3); 00109 }
ContextLogger2—ContextLogger2 Logger Daemon Internals—Generated on Mon May 2 13:49:56 2011 by Doxygen 1.6.1