/* * Copyright (C) 2005 Network Applied Communication Laboratory Co., Ltd. * * This file is part of Rast. * See the file COPYING for redistribution information. * */ #include #include #include "rast/ruby.h" #include "rast/rast.h" VALUE rast_rb_cDate, rast_rb_cDateTime; VALUE rast_rb_mRast; VALUE rast_rb_eError, rast_rb_eRastError, rast_rb_eAprError; VALUE rast_rb_eBDBError, rast_rb_eXMLRPCError, rast_rb_eRubyError; static VALUE cPool, cBucketAlloc; static VALUE cBrigade, cBucket; static VALUE cEOSBucket, cFileBucket, cPipeBucket, cTransientBucket; void rast_rb_raise_error(rast_error_t *error) { VALUE error_class; char buf[RAST_BUFSIZ]; if (error == RAST_OK) { return; } switch (error->type) { case RAST_ERROR_TYPE_RAST: error_class = rast_rb_eRastError; break; case RAST_ERROR_TYPE_APR: error_class = rast_rb_eAprError; break; case RAST_ERROR_TYPE_BDB: error_class = rast_rb_eBDBError; break; case RAST_ERROR_TYPE_XMLRPC: error_class = rast_rb_eXMLRPCError; break; case RAST_ERROR_TYPE_RUBY: error_class = rast_rb_eRubyError; break; default: error_class = rast_rb_eError; }; #ifdef RAST_DEBUG snprintf(buf, RAST_BUFSIZ, "%s:%d: %s", error->file, error->line, error->message); #else strncpy(buf, error->message, RAST_BUFSIZ); #endif buf[RAST_BUFSIZ - 1] = '\0'; rast_error_destroy(error); rb_raise(error_class, "%s", buf); } rast_error_t * rast_rb_exception_to_rast_error(VALUE obj) { VALUE message, backtrace; message = rb_funcall(obj, rb_intern("message"), 0, NULL); backtrace = rb_funcall(obj, rb_intern("backtrace"), 0, NULL); if (NIL_P(backtrace)) { return rast_error_create(RAST_ERROR_TYPE_RUBY, RAST_ERROR_GENERAL, "%s", StringValuePtr(message)); } else { VALUE first_backtrace; first_backtrace = rb_ary_entry(backtrace, 0); return rast_error_create(RAST_ERROR_TYPE_RUBY, RAST_ERROR_GENERAL, "%s: %s", StringValuePtr(first_backtrace), StringValuePtr(message)); } } static VALUE ruby_false(VALUE self) { return Qfalse; } static VALUE ruby_true(VALUE self) { return Qtrue; } static void pool_free(apr_pool_t *pool) { if (pool) { apr_pool_destroy(pool); } } static VALUE pool_alloc(VALUE klass) { return Data_Wrap_Struct(klass, NULL, pool_free, NULL); } static int raise_on_pool_failure(int retcode) { rb_memerror(); return -1; /* prevent compiler warnings */ } static VALUE pool_initialize(VALUE self) { apr_status_t status; apr_pool_t *pool; status = apr_pool_create_ex(&pool, NULL, raise_on_pool_failure, NULL); if (status != APR_SUCCESS) { rb_memerror(); } DATA_PTR(self) = pool; return Qnil; } static apr_pool_t * get_pool(VALUE self) { if (TYPE(self) != T_DATA || RDATA(self)->dfree != (RUBY_DATA_FUNC) pool_free) { rb_raise(rb_eTypeError, "wrong argument type %s (expected Rast::Pool)", rb_obj_classname(self)); } return (apr_pool_t *) DATA_PTR(self); } apr_pool_t * rast_rb_pool_new(VALUE *vpool) { *vpool = pool_alloc(cPool); pool_initialize(*vpool); return get_pool(*vpool); } apr_status_t rast_rb_pool_create_ex(apr_pool_t **new_pool, apr_pool_t *parent, apr_allocator_t *allocator) { return apr_pool_create_ex(new_pool, parent, raise_on_pool_failure, allocator); } char * rast_rb_hash_get_string(apr_pool_t *pool, VALUE hash, const char *name) { VALUE val; val = rb_hash_aref(hash, rb_str_new2(name)); SafeStringValue(val); return apr_pstrdup(pool, RSTRING(val)->ptr); } int rast_rb_hash_get_bool(VALUE hash, const char *name) { VALUE val; val = rb_hash_aref(hash, rb_str_new2(name)); return RTEST(val); } rast_type_e rast_rb_hash_get_property_type(VALUE hash, const char *name) { VALUE val; val = rb_hash_aref(hash, rb_str_new2(name)); return NUM2INT(val); } void rast_rb_get_bool_option(VALUE options, const char *name, int *dst) { VALUE value = rb_hash_aref(options, rb_str_new2(name)); if (!NIL_P(value)) { *dst = RTEST(value); } } void rast_rb_get_int_option(VALUE options, const char *name, int *dst) { VALUE value = rb_hash_aref(options, rb_str_new2(name)); if (RTEST(value)) { *dst = NUM2INT(value); } } void rast_rb_get_string_option(VALUE options, const char *name, const char **dst) { VALUE value = rb_hash_aref(options, rb_str_new2(name)); if (RTEST(value)) { SafeStringValue(value); *dst = RSTRING(value)->ptr; } } const char * rast_rb_get_safe_string_ptr(VALUE str) { if (NIL_P(str)) { return NULL; } SafeStringValue(str); return StringValuePtr(str); } void rast_rb_db_free(rast_rb_db_data_t *data) { if (data) { if (!data->closed) { rast_db_close(data->db); } apr_pool_destroy(data->pool); xfree(data); } } VALUE rast_rb_process_db_initialize(int argc, VALUE *argv, VALUE self, rast_error_t *(*open)(rast_db_t **db, const char *name, int flags, rast_db_open_option_t * options, apr_pool_t *pool)) { VALUE name, vflags, voptions, vpool; rast_error_t *error; apr_pool_t *pool, *tmp_pool; rast_rb_db_data_t *data; rast_db_t *db; int flags = RAST_DB_RDWR; rast_db_open_option_t *options = NULL; rb_scan_args(argc, argv, "12", &name, &vflags, &voptions); if (!NIL_P(vflags)) { flags = NUM2INT(vflags); } if (!NIL_P(voptions)) { tmp_pool = rast_rb_pool_new(&vpool); options = rast_db_open_option_create(tmp_pool); rast_rb_get_int_option(voptions, "sync_threshold_chars", &options->sync_threshold_chars); } rast_rb_pool_create_ex(&pool, NULL, NULL); SafeStringValue(name); error = open(&db, RSTRING(name)->ptr, flags, options, pool); if (error != RAST_OK) { apr_pool_destroy(pool); rast_rb_raise_error(error); } data = ALLOC(rast_rb_db_data_t); data->db = db; data->pool = pool; data->closed = 0; DATA_PTR(self) = data; return Qnil; } rast_db_t * rast_rb_get_db(VALUE self) { rast_rb_db_data_t *data; if (TYPE(self) != T_DATA || RDATA(self)->dfree != (RUBY_DATA_FUNC) rast_rb_db_free) { rb_raise(rb_eTypeError, "wrong argument type %s (expected Rast::DB)", rb_obj_classname(self)); } data = DATA_PTR(self); if (data->closed) { rb_raise(rast_rb_eError, "already closed db"); } return data->db; } typedef struct { apr_bucket *bucket; VALUE mark_value; VALUE vpool; } bucket_data_t; static void bucket_mark(bucket_data_t *data) { if (data == NULL) { return; } rb_gc_mark(data->mark_value); rb_gc_mark(data->vpool); } static VALUE bucket_alloc(VALUE klass) { bucket_data_t *data; VALUE vpool; apr_pool_t *pool; pool = rast_rb_pool_new(&vpool); data = (bucket_data_t *) apr_palloc(pool, sizeof(bucket_data_t)); data->bucket = NULL; data->mark_value = Qnil; data->vpool = vpool; return Data_Wrap_Struct(klass, bucket_mark, NULL, data); } static inline bucket_data_t * get_bucket_data(VALUE vbucket) { return (bucket_data_t *) DATA_PTR(vbucket); } static VALUE bucket_new(apr_bucket *bucket) { VALUE klass, vbucket; bucket_data_t *data; if (APR_BUCKET_IS_EOS(bucket)) { klass = cEOSBucket; } else if (APR_BUCKET_IS_FILE(bucket)) { klass = cFileBucket; } else if (APR_BUCKET_IS_TRANSIENT(bucket)) { klass = cTransientBucket; } else { klass = cBucket; } vbucket = bucket_alloc(klass); data = get_bucket_data(vbucket); data->bucket = bucket; return vbucket; } static apr_bucket * get_bucket(VALUE vbucket) { return get_bucket_data(vbucket)->bucket; } static VALUE bucket_read(VALUE self) { apr_bucket *bucket = get_bucket(self); const char *buf; int nbytes; apr_bucket_read(bucket, &buf, &nbytes, APR_BLOCK_READ); return rb_str_new(buf, nbytes); } static VALUE eos_bucket_initialize(VALUE self) { apr_bucket_alloc_t *bucket_alloc; bucket_data_t *data; apr_pool_t *pool; data = get_bucket_data(self); pool = get_pool(data->vpool); bucket_alloc = apr_bucket_alloc_create(pool); data->bucket = apr_bucket_eos_create(bucket_alloc); return Qnil; } static VALUE file_bucket_initialize(VALUE self, VALUE vfile) { apr_bucket_alloc_t *bucket_alloc; bucket_data_t *data; apr_pool_t *pool; struct OpenFile *fptr; apr_file_t *file; int fd; apr_finfo_t finfo; apr_status_t status; data = get_bucket_data(self); pool = get_pool(data->vpool); bucket_alloc = apr_bucket_alloc_create(pool); Check_Type(vfile, T_FILE); GetOpenFile(vfile, fptr); status = apr_stat(&finfo, fptr->path, APR_FINFO_SIZE, pool); if (status != APR_SUCCESS) { rast_rb_raise_error(apr_status_to_rast_error(status)); } fd = fileno(fptr->f); status = apr_os_file_put(&file, &fd, 0, pool); if (status != APR_SUCCESS) { rast_rb_raise_error(apr_status_to_rast_error(status)); } data->bucket = apr_bucket_file_create(file, 0, finfo.size, pool, bucket_alloc); data->mark_value = vfile; return Qnil; } static VALUE pipe_bucket_initialize(VALUE self, VALUE vpout) { apr_bucket_alloc_t *bucket_alloc; bucket_data_t *data; apr_pool_t *pool; struct OpenFile *fptr; apr_file_t *pipe; int fd; apr_status_t status; data = get_bucket_data(self); pool = get_pool(data->vpool); bucket_alloc = apr_bucket_alloc_create(pool); if (CLASS_OF(vpout) != rb_cIO) { rb_raise(rb_eTypeError, "wrong argument type %s (expected IO)", rb_obj_classname(vpout)); } GetOpenFile(vpout, fptr); fd = fileno(fptr->f); status = apr_os_file_put(&pipe, &fd, 0, pool); if (status != APR_SUCCESS) { rast_rb_raise_error(apr_status_to_rast_error(status)); } data->bucket = apr_bucket_pipe_create(pipe, bucket_alloc); return Qnil; } static VALUE transient_bucket_initialize(VALUE self, VALUE str) { apr_bucket_alloc_t *bucket_alloc; bucket_data_t *data; const char *buf; int buf_nbytes; apr_pool_t *pool; SafeStringValue(str); data = get_bucket_data(self); pool = get_pool(data->vpool); buf_nbytes = RSTRING(str)->len; buf = (char *) apr_pmemdup(pool, RSTRING(str)->ptr, buf_nbytes); bucket_alloc = apr_bucket_alloc_create(pool); data->bucket = apr_bucket_transient_create(buf, buf_nbytes, bucket_alloc); return Qnil; } typedef struct { apr_bucket_brigade *brigade; VALUE vbuckets; VALUE vpool; } brigade_data_t; static void brigade_mark(brigade_data_t *data) { if (data == NULL) { return; } rb_gc_mark(data->vbuckets); rb_gc_mark(data->vpool); } static VALUE brigade_alloc() { brigade_data_t *data; VALUE vpool; apr_pool_t *pool; pool = rast_rb_pool_new(&vpool); data = (brigade_data_t *) apr_palloc(pool, sizeof(brigade_data_t)); data->brigade = NULL; data->vbuckets = rb_ary_new(); data->vpool = vpool; return Data_Wrap_Struct(cBrigade, brigade_mark, NULL, data); } static inline brigade_data_t * get_brigade_data(VALUE vbrigade) { return (brigade_data_t *) DATA_PTR(vbrigade); } VALUE rast_rb_brigade_new(apr_bucket_brigade *brigade) { VALUE vbrigade; vbrigade = brigade_alloc(); get_brigade_data(vbrigade)->brigade = brigade; return vbrigade; } apr_bucket_brigade * rast_rb_get_brigade(VALUE vbrigade) { return get_brigade_data(vbrigade)->brigade; } static VALUE brigade_insert_tail(VALUE self, VALUE vbucket); static VALUE brigade_initialize(int argc, VALUE *argv, VALUE self) { brigade_data_t *data = get_brigade_data(self); apr_bucket_alloc_t *bucket_alloc; apr_pool_t *pool = get_pool(data->vpool); int i; bucket_alloc = apr_bucket_alloc_create(pool); data->brigade = apr_brigade_create(pool, bucket_alloc); for (i = 0; i < argc; i++) { if (!rb_obj_is_kind_of(argv[i], cBucket)) { rb_raise(rb_eTypeError, "wrong argument type %s (expected Rast::Bucket)", rb_obj_classname(argv[i])); } brigade_insert_tail(self, argv[i]); } return Qnil; } static VALUE brigade_insert_tail(VALUE self, VALUE vbucket) { brigade_data_t *data = get_brigade_data(self); apr_bucket *bucket = get_bucket(vbucket); APR_BRIGADE_INSERT_TAIL(data->brigade, bucket); rb_ary_push(data->vbuckets, vbucket); return Qnil; } static VALUE brigade_each(VALUE self) { apr_bucket_brigade *brigade = rast_rb_get_brigade(self); apr_bucket *bucket; VALUE vbucket; for (bucket = APR_BRIGADE_FIRST(brigade); bucket != APR_BRIGADE_SENTINEL(brigade); bucket = APR_BUCKET_NEXT(bucket)) { vbucket = bucket_new(bucket); rb_yield(vbucket); } return Qnil; } void rast_rb_initialize() { static int initialized = 0; if (initialized) { return; } initialized = 1; rb_require("date"); rast_rb_cDate = rb_const_get(rb_mKernel, rb_intern("Date")); rast_rb_cDateTime = rb_const_get(rb_mKernel, rb_intern("DateTime")); rast_rb_mRast = rb_define_module("Rast"); rast_rb_eError = rb_define_class_under(rast_rb_mRast, "Error", rb_eStandardError); rb_define_const(rast_rb_eError, "TYPE_RAST", INT2NUM(RAST_ERROR_TYPE_RAST)); rb_define_const(rast_rb_eError, "TYPE_APR", INT2NUM(RAST_ERROR_TYPE_APR)); rb_define_const(rast_rb_eError, "TYPE_BDB", INT2NUM(RAST_ERROR_TYPE_BDB)); rast_rb_eRastError = rb_define_class_under(rast_rb_mRast, "RastError", rast_rb_eError); rast_rb_eAprError = rb_define_class_under(rast_rb_mRast, "AprError", rast_rb_eError); rast_rb_eBDBError = rb_define_class_under(rast_rb_mRast, "BDBError", rast_rb_eError); rast_rb_eXMLRPCError = rb_define_class_under(rast_rb_mRast, "XMLRPCError", rast_rb_eError); rast_rb_eRubyError = rb_define_class_under(rast_rb_mRast, "RubyError", rast_rb_eError); cPool = rb_define_class_under(rast_rb_mRast, "Pool", rb_cObject); rb_define_alloc_func(cPool, pool_alloc); rb_define_method(cPool, "initialize", pool_initialize, 0); rb_define_const(rast_rb_mRast, "VERSION", rb_str_new2(VERSION)); cBucketAlloc = rb_define_class_under(rast_rb_mRast, "BucketAlloc", rb_cObject); cBucket = rb_define_class_under(rast_rb_mRast, "Bucket", rb_cObject); rb_define_alloc_func(cBucket, bucket_alloc); rb_define_method(cBucket, "read", bucket_read, 0); rb_define_method(cBucket, "eos?", ruby_false, 0); rb_define_method(cBucket, "file?", ruby_false, 0); rb_define_method(cBucket, "pipe?", ruby_false, 0); rb_define_method(cBucket, "transient?", ruby_false, 0); cTransientBucket = rb_define_class_under(rast_rb_mRast, "TransientBucket", cBucket); rb_define_method(cTransientBucket, "initialize", transient_bucket_initialize, 1); rb_define_method(cTransientBucket, "transient?", ruby_true, 0); cFileBucket = rb_define_class_under(rast_rb_mRast, "FileBucket", cBucket); rb_define_method(cFileBucket, "initialize", file_bucket_initialize, 1); rb_define_method(cFileBucket, "file?", ruby_true, 0); cPipeBucket = rb_define_class_under(rast_rb_mRast, "PipeBucket", cBucket); rb_define_method(cPipeBucket, "initialize", pipe_bucket_initialize, 1); rb_define_method(cPipeBucket, "pipe?", ruby_true, 0); cEOSBucket = rb_define_class_under(rast_rb_mRast, "EOSBucket", cBucket); rb_define_method(cEOSBucket, "initialize", eos_bucket_initialize, 0); rb_define_method(cEOSBucket, "eos?", ruby_true, 0); cBrigade = rb_define_class_under(rast_rb_mRast, "Brigade", rb_cObject); rb_define_alloc_func(cBrigade, brigade_alloc); rb_define_method(cBrigade, "initialize", brigade_initialize, -1); rb_define_method(cBrigade, "insert_tail", brigade_insert_tail, 1); rb_define_method(cBrigade, "each", brigade_each, 0); }