pax_global_header00006660000000000000000000000064120444437070014517gustar00rootroot0000000000000052 comment=f6434168b8ad9b2e0c0fb6d522ce43532e2c0a58 ruby-patron-0.4.18/000077500000000000000000000000001204444370700140735ustar00rootroot00000000000000ruby-patron-0.4.18/.autotest000066400000000000000000000005231204444370700157440ustar00rootroot00000000000000Autotest.add_hook :initialize do |at| at.clear_mappings at.add_mapping(%r{^spec/.+_spec\.rb$}) do |filename,_| filename end at.add_mapping(%r{^lib/patron/(.+)\.rb$}) do |_,match| [ "spec/#{match[1]}_spec.rb" ] end at.add_mapping(%r{^spec/spec_helper\.rb$}) do at.files_matching(%r{^spec/.+_spec\.rb$}) end end ruby-patron-0.4.18/.gitignore000066400000000000000000000000621204444370700160610ustar00rootroot00000000000000.rbenv* .rvmrc *.bundle coverage rdoc doc pkg tmp ruby-patron-0.4.18/.rspec000066400000000000000000000000001204444370700151760ustar00rootroot00000000000000ruby-patron-0.4.18/Gemfile000066400000000000000000000000311204444370700153600ustar00rootroot00000000000000source :rubygems gemspec ruby-patron-0.4.18/Gemfile.lock000066400000000000000000000010201204444370700163060ustar00rootroot00000000000000PATH remote: . specs: patron (0.4.18) GEM remote: http://rubygems.org/ specs: diff-lcs (1.1.3) rake (0.9.2.2) rake-compiler (0.7.9) rake rcov (0.9.11) rspec (2.7.0) rspec-core (~> 2.7.0) rspec-expectations (~> 2.7.0) rspec-mocks (~> 2.7.0) rspec-core (2.7.1) rspec-expectations (2.7.0) diff-lcs (~> 1.1.2) rspec-mocks (2.7.0) PLATFORMS ruby DEPENDENCIES bundler (>= 1.0.0) patron! rake-compiler (>= 0.7.5) rcov (>= 0.9.9) rspec (>= 2.3.0) ruby-patron-0.4.18/LICENSE000066400000000000000000000020641204444370700151020ustar00rootroot00000000000000Copyright (c) 2008 The Hive http://www.thehive.com/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ruby-patron-0.4.18/README.txt000066400000000000000000000026701204444370700155760ustar00rootroot00000000000000= Ruby HTTP Client == SYNOPSIS Patron is a Ruby HTTP client library based on libcurl. It does not try to expose the full "power" (read complexity) of libcurl but instead tries to provide a sane API while taking advantage of libcurl under the hood. == USAGE Usage is very simple. First, you instantiate a Session object. You can set a few default options on the Session instance that will be used by all subsequent requests: sess = Patron::Session.new sess.timeout = 10 sess.base_url = "http://myserver.com:9900" sess.headers['User-Agent'] = 'myapp/1.0' sess.enable_debug "/tmp/patron.debug" The Session is used to make HTTP requests. resp = sess.get("/foo/bar") Requests return a Response object: if resp.status < 400 puts resp.body end The GET, HEAD, PUT, POST and DELETE operations are all supported. sess.put("/foo/baz", "some data") sess.delete("/foo/baz") You can ship custom headers with a single request: sess.post("/foo/stuff", "some data", {"Content-Type" => "text/plain"}) That is pretty much all there is to it. == REQUIREMENTS You need a recent version of libcurl in order to install this gem. On MacOS X the provided libcurl is sufficient. You will have to install the libcurl development packages on Debian or Ubuntu. Other Linux systems are probably similar. Windows users are on your own. Good luck with that. == INSTALL sudo gem install patron Copyright (c) 2008 The Hive ruby-patron-0.4.18/Rakefile000066400000000000000000000046571204444370700155540ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require 'rake/clean' require 'rake/rdoctask' require 'rake/extensiontask' require 'rspec/core/rake_task' require 'bundler' Rake::ExtensionTask.new do |ext| ext.name = 'session_ext' # indicate the name of the extension. ext.ext_dir = 'ext/patron' # search for 'hello_world' inside it. ext.lib_dir = 'lib/patron' # put binaries into this folder. end Bundler::GemHelper.install_tasks CLEAN.include FileList["ext/patron/*"].exclude(/^.*\.(rb|c|h)$/) CLOBBER.include %w( doc coverage pkg ) desc "Start an IRB shell" task :shell => :compile do sh 'irb -I./lib -I./ext -r patron' end Rake::RDocTask.new do |rdoc| rdoc.rdoc_dir = 'rdoc' rdoc.title = 'Patron documentation' rdoc.main = 'README.txt' rdoc.rdoc_files.include('README.txt') rdoc.rdoc_files.include('lib/**/*.rb') end desc "Run specs" RSpec::Core::RakeTask.new do |t| t.rspec_opts = %w( --colour --format progress ) t.pattern = 'spec/**/*_spec.rb' end task :spec => [:compile] desc "Run specs with RCov" RSpec::Core::RakeTask.new('spec:rcov') do |t| t.pattern = 'spec/**/*_spec.rb' t.rcov = true t.rcov_opts = %q(--sort coverage --comments --exclude "spec") end task :default => :spec ruby-patron-0.4.18/ext/000077500000000000000000000000001204444370700146735ustar00rootroot00000000000000ruby-patron-0.4.18/ext/patron/000077500000000000000000000000001204444370700161765ustar00rootroot00000000000000ruby-patron-0.4.18/ext/patron/.gitignore000066400000000000000000000000371204444370700201660ustar00rootroot00000000000000*.bundle *.o mkmf.log Makefile ruby-patron-0.4.18/ext/patron/extconf.rb000066400000000000000000000035131204444370700201730ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require 'mkmf' require 'rbconfig' dir_config('curl') if find_executable('curl-config') $CFLAGS << " #{`curl-config --cflags`.strip}" $LIBS << " #{`curl-config --libs`.strip}" elsif !have_library('curl') or !have_header('curl/curl.h') fail <<-EOM Can't find libcurl or curl/curl.h Try passing --with-curl-dir or --with-curl-lib and --with-curl-include options to extconf. EOM end if CONFIG['CC'] =~ /gcc/ $CFLAGS << ' -pedantic -Wall' end $defs.push("-DUSE_TBR") $defs.push("-DHAVE_TBR") if have_func('rb_thread_blocking_region') create_makefile 'patron/session_ext' ruby-patron-0.4.18/ext/patron/membuffer.c000066400000000000000000000037461204444370700203240ustar00rootroot00000000000000#include #include #include "membuffer.h" #define DEFAULT_CAPACITY 4096 #define MAXVAL(a, b) ((a) > (b) ? (a) : (b)) static int membuffer_ensure_capacity( membuffer* m, size_t length ) { size_t new_capacity; char* tmp_buf; if (m->capacity >= length) { return MB_OK; } new_capacity = MAXVAL(m->capacity, DEFAULT_CAPACITY); while (new_capacity < length) { new_capacity *= 2; } tmp_buf = ruby_xrealloc(m->buf, new_capacity+1); if (NULL == tmp_buf) { return MB_OUT_OF_MEMORY; } else { m->buf = tmp_buf; m->capacity = new_capacity; } return MB_OK; } void membuffer_init( membuffer* m ) { assert(NULL != m); m->buf = NULL; m->length = 0; m->capacity = 0; } void membuffer_destroy( membuffer* m ) { if (NULL == m) { return; } if (NULL != m->buf) { ruby_xfree(m->buf); } m->buf = NULL; m->length = 0; m->capacity = 0; } void membuffer_clear( membuffer* m ) { assert(NULL != m); if (NULL != m->buf) { memset(m->buf, 0, m->capacity+1); m->length = 0; } } int membuffer_insert( membuffer* m, size_t index, const void* src, size_t length ) { int rc = MB_OK; assert(NULL != m); /* sanity checks on the inputs */ if (index > m->length) { return MB_OUT_OF_BOUNDS; } if (NULL == src || 0 == length) { return MB_OK; } /* increase capacity if needed */ rc = membuffer_ensure_capacity( m, m->length + length ); if (MB_OK != rc) { return rc; } /* move data in the buffer to the right of the insertion point */ memmove( m->buf + index + length, m->buf + index, m->length - index ); /* copy date into the insertion point */ memcpy( m->buf + index, src, length ); m->length += length; m->buf[m->length] = 0; /* null terminate the buffer */ return MB_OK; } int membuffer_append( membuffer* m, const void* src, size_t length ) { assert(NULL != m); return membuffer_insert( m, m->length, src, length ); } VALUE membuffer_to_rb_str( membuffer* m ) { assert(NULL != m); return rb_str_new(m->buf, m->length); } ruby-patron-0.4.18/ext/patron/membuffer.h000066400000000000000000000041261204444370700203220ustar00rootroot00000000000000 #ifndef PATRON_MEMBUFER_H #define PATRON_MEMBUFER_H #include #define MB_OK 0 #define MB_OUT_OF_MEMORY 1 #define MB_OUT_OF_BOUNDS 2 /** * Implementation of a simple memory buffer for collecting the response body * and headers from a curl request. The memory buffer will grow to accomodate * the data inserted into it. * * When the memory buffer needs more capacity, it will reallocate memory from * the heap. It will request twice it's current capacity. */ typedef struct { char *buf; size_t length; size_t capacity; } membuffer; /** * Initialize the memory buffer by setting default values of 0 for the length * and capacity. */ void membuffer_init( membuffer* m ); /** * Free any memory used by the memory buffer. */ void membuffer_destroy( membuffer* m ); /** * Clear the contents of the memory buffer. The length will be set to zero, * but the capacity will remain unchanged - i.e. memory will not be freed by * his method. */ void membuffer_clear( membuffer* m ); /** * Attempt to insert the given _src_ data into the memory buffer at the given * _index_. This method will shift data in the memory buffer to the right in * order to insert the _src_ data. * * This method will fail if the _index_ is out of bounds for the memory * buffer, if the _src_ is NULL or the _length_ to insert is 0. This method * can also fail if the memory buffer needs to expand it's capacity but no * memory is available. * * Return Codes: * MB_OK * MB_OUT_OF_MEMORY * MB_OUT_OF_BOUNDS */ int membuffer_insert( membuffer* m, size_t index, const void* src, size_t length ); /** * Append the given _src_ data to the end of the memory buffer. This method * calls `membuffer_insert` to append the data. * * Return Codes: * MB_OK * MB_OUT_OF_MEMORY */ int membuffer_append( membuffer* m, const void* src, size_t length ); /** * Convert the memory buffer into a Ruby String instance. This method will * return an empty String instance if the memory buffer is empty. This method * will never return Qnil. */ VALUE membuffer_to_rb_str( membuffer* m ); #endif ruby-patron-0.4.18/ext/patron/session_ext.c000066400000000000000000000575421204444370700207220ustar00rootroot00000000000000/* -------------------------------------------------------------------------- *\ * * Patron HTTP Client: Interface to libcurl * Copyright (c) 2008 The Hive http://www.thehive.com/ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * \* -------------------------------------------------------------------------- */ #include #include #include "membuffer.h" #include "sglib.h" /* Simple Generic Library -> http://sglib.sourceforge.net */ #define UNUSED_ARGUMENT(x) (void)x static VALUE mPatron = Qnil; static VALUE mProxyType = Qnil; static VALUE cSession = Qnil; static VALUE cRequest = Qnil; static VALUE ePatronError = Qnil; static VALUE eUnsupportedProtocol = Qnil; static VALUE eURLFormatError = Qnil; static VALUE eHostResolutionError = Qnil; static VALUE eConnectionFailed = Qnil; static VALUE ePartialFileError = Qnil; static VALUE eTimeoutError = Qnil; static VALUE eTooManyRedirects = Qnil; struct curl_state { CURL* handle; char* upload_buf; FILE* download_file; FILE* upload_file; FILE* debug_file; char error_buf[CURL_ERROR_SIZE]; struct curl_slist* headers; struct curl_httppost* post; struct curl_httppost* last; membuffer header_buffer; membuffer body_buffer; int interrupt; }; /*----------------------------------------------------------------------------*/ /* Curl Callbacks */ /* Takes data streamed from libcurl and writes it to a Ruby string buffer. */ static size_t session_write_handler(char* stream, size_t size, size_t nmemb, membuffer* buf) { int rc = membuffer_append(buf, stream, size * nmemb); /* return 0 to signal that we could not append data to our buffer */ if (MB_OK != rc) { return 0; } /* otherwise, return the number of bytes appended */ return size * nmemb; } static size_t session_read_handler(char* stream, size_t size, size_t nmemb, char **buffer) { size_t result = 0; if (buffer != NULL && *buffer != NULL) { size_t len = size * nmemb; char *s1 = strncpy(stream, *buffer, len); result = strlen(s1); *buffer += result; } return result; } /* A non-zero return value from the progress handler will terminate the current * request. We use this fact in order to interrupt any request when either the * user calls the "interrupt" method on the session or when the Ruby interpreter * is attempting to exit. */ static int session_progress_handler(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { struct curl_state* state = (struct curl_state*) clientp; UNUSED_ARGUMENT(dltotal); UNUSED_ARGUMENT(dlnow); UNUSED_ARGUMENT(ultotal); UNUSED_ARGUMENT(ulnow); return state->interrupt; } /*----------------------------------------------------------------------------*/ /* List of active curl sessions */ struct curl_state_list { struct curl_state *state; struct curl_state_list *next; }; #define CS_LIST_COMPARATOR(p, _state_) (p->state == _state_) static struct curl_state_list *cs_list = NULL; static void cs_list_append( struct curl_state *state ) { struct curl_state_list *item = NULL; assert(state != NULL); item = ruby_xmalloc(sizeof(struct curl_state_list)); item->state = state; item->next = NULL; SGLIB_LIST_ADD(struct curl_state_list, cs_list, item, next); } static void cs_list_remove(struct curl_state *state) { struct curl_state_list *item = NULL; assert(state != NULL); SGLIB_LIST_FIND_MEMBER(struct curl_state_list, cs_list, state, CS_LIST_COMPARATOR, next, item); if (item) { SGLIB_LIST_DELETE(struct curl_state_list, cs_list, item, next); ruby_xfree(item); } } static void cs_list_interrupt(VALUE data) { UNUSED_ARGUMENT(data); SGLIB_LIST_MAP_ON_ELEMENTS(struct curl_state_list, cs_list, item, next, { item->state->interrupt = 1; }); } /*----------------------------------------------------------------------------*/ /* Object allocation */ static void session_close_debug_file(struct curl_state *curl) { if (curl->debug_file && stderr != curl->debug_file) { fclose(curl->debug_file); } curl->debug_file = NULL; } /* Cleans up the Curl handle when the Session object is garbage collected. */ void session_free(struct curl_state *curl) { if (curl->handle) { curl_easy_cleanup(curl->handle); curl->handle = NULL; } session_close_debug_file(curl); membuffer_destroy( &curl->header_buffer ); membuffer_destroy( &curl->body_buffer ); cs_list_remove(curl); free(curl); } /* Allocates curl_state data needed for a new Session object. */ VALUE session_alloc(VALUE klass) { struct curl_state* curl; VALUE obj = Data_Make_Struct(klass, struct curl_state, NULL, session_free, curl); membuffer_init( &curl->header_buffer ); membuffer_init( &curl->body_buffer ); cs_list_append(curl); return obj; } /* Return the curl_state from the ruby VALUE which is the Session instance. */ static struct curl_state* get_curl_state(VALUE self) { struct curl_state *state; Data_Get_Struct(self, struct curl_state, state); if (NULL == state->handle) { state->handle = curl_easy_init(); curl_easy_setopt(state->handle, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(state->handle, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(state->handle, CURLOPT_PROGRESSFUNCTION, &session_progress_handler); curl_easy_setopt(state->handle, CURLOPT_PROGRESSDATA, state); } return state; } /*----------------------------------------------------------------------------*/ /* Method implementations */ /* call-seq: * Patron.libcurl_version -> version string * * Returns the version of the embedded libcurl as a string. */ static VALUE libcurl_version(VALUE klass) { char* value = curl_version(); UNUSED_ARGUMENT(klass); return rb_str_new2(value); } /* call-seq: * Session.escape( string ) -> escaped string * * URL escapes the provided string. */ static VALUE session_escape(VALUE self, VALUE value) { struct curl_state *state = get_curl_state(self); VALUE string = StringValue(value); char* escaped = NULL; VALUE retval = Qnil; escaped = curl_easy_escape(state->handle, RSTRING_PTR(string), (int) RSTRING_LEN(string)); retval = rb_str_new2(escaped); curl_free(escaped); return retval; } /* call-seq: * Session.unescape( string ) -> unescaped string * * Unescapes the provided string. */ static VALUE session_unescape(VALUE self, VALUE value) { struct curl_state *state = get_curl_state(self); VALUE string = StringValue(value); char* unescaped = NULL; VALUE retval = Qnil; unescaped = curl_easy_unescape(state->handle, RSTRING_PTR(string), (int) RSTRING_LEN(string), NULL); retval = rb_str_new2(unescaped); curl_free(unescaped); return retval; } /* Callback used to iterate over the HTTP headers and store them in an slist. */ static int each_http_header(VALUE header_key, VALUE header_value, VALUE self) { struct curl_state *state = get_curl_state(self); VALUE name = rb_obj_as_string(header_key); VALUE value = rb_obj_as_string(header_value); VALUE header_str = Qnil; header_str = rb_str_plus(name, rb_str_new2(": ")); header_str = rb_str_plus(header_str, value); state->headers = curl_slist_append(state->headers, StringValuePtr(header_str)); return 0; } static int formadd_values(VALUE data_key, VALUE data_value, VALUE self) { struct curl_state *state = get_curl_state(self); VALUE name = rb_obj_as_string(data_key); VALUE value = rb_obj_as_string(data_value); curl_formadd(&state->post, &state->last, CURLFORM_PTRNAME, RSTRING_PTR(name), CURLFORM_PTRCONTENTS, RSTRING_PTR(value), CURLFORM_END); return 0; } static int formadd_files(VALUE data_key, VALUE data_value, VALUE self) { struct curl_state *state = get_curl_state(self); VALUE name = rb_obj_as_string(data_key); VALUE value = rb_obj_as_string(data_value); curl_formadd(&state->post, &state->last, CURLFORM_PTRNAME, RSTRING_PTR(name), CURLFORM_FILE, RSTRING_PTR(value), CURLFORM_END); return 0; } static void set_chunked_encoding(struct curl_state *state) { state->headers = curl_slist_append(state->headers, "Transfer-Encoding: chunked"); } static FILE* open_file(VALUE filename, const char* perms) { FILE* handle = fopen(StringValuePtr(filename), perms); if (!handle) { rb_raise(rb_eArgError, "Unable to open specified file."); } return handle; } /* Set the options on the Curl handle from a Request object. Takes each field * in the Request object and uses it to set the appropriate option on the Curl * handle. */ static void set_options_from_request(VALUE self, VALUE request) { struct curl_state *state = get_curl_state(self); CURL* curl = state->handle; ID action = Qnil; VALUE headers = Qnil; VALUE url = Qnil; VALUE timeout = Qnil; VALUE redirects = Qnil; VALUE proxy = Qnil; VALUE proxy_type = Qnil; VALUE credentials = Qnil; VALUE ignore_content_length = Qnil; VALUE insecure = Qnil; VALUE buffer_size = Qnil; headers = rb_iv_get(request, "@headers"); if (!NIL_P(headers)) { if (rb_type(headers) != T_HASH) { rb_raise(rb_eArgError, "Headers must be passed in a hash."); } rb_hash_foreach(headers, each_http_header, self); } action = SYM2ID(rb_iv_get(request, "@action")); if (action == rb_intern("get")) { VALUE data = rb_iv_get(request, "@upload_data"); VALUE download_file = rb_iv_get(request, "@file_name"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); if (!NIL_P(data)) { long len = RSTRING_LEN(data); state->upload_buf = StringValuePtr(data); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); curl_easy_setopt(curl, CURLOPT_READFUNCTION, &session_read_handler); curl_easy_setopt(curl, CURLOPT_READDATA, &state->upload_buf); curl_easy_setopt(curl, CURLOPT_INFILESIZE, len); } if (!NIL_P(download_file)) { state->download_file = open_file(download_file, "w"); curl_easy_setopt(curl, CURLOPT_WRITEDATA, state->download_file); } else { state->download_file = NULL; } } else if (action == rb_intern("post") || action == rb_intern("put")) { VALUE data = rb_iv_get(request, "@upload_data"); VALUE filename = rb_iv_get(request, "@file_name"); VALUE multipart = rb_iv_get(request, "@multipart"); if (!NIL_P(data) && NIL_P(multipart)) { long len = RSTRING_LEN(data); state->upload_buf = StringValuePtr(data); if (action == rb_intern("post")) { curl_easy_setopt(curl, CURLOPT_POST, 1); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, state->upload_buf); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len); } else { curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); curl_easy_setopt(curl, CURLOPT_READFUNCTION, &session_read_handler); curl_easy_setopt(curl, CURLOPT_READDATA, &state->upload_buf); curl_easy_setopt(curl, CURLOPT_INFILESIZE, len); } } else if (!NIL_P(filename) && NIL_P(multipart)) { set_chunked_encoding(state); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1); if (action == rb_intern("post")) { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); } state->upload_file = open_file(filename, "r"); curl_easy_setopt(curl, CURLOPT_READDATA, state->upload_file); } else if (!NIL_P(multipart)) { if (action == rb_intern("post")) { if(!NIL_P(data) && !NIL_P(filename)) { if (rb_type(data) == T_HASH && rb_type(filename) == T_HASH) { rb_hash_foreach(data, formadd_values, self); rb_hash_foreach(filename, formadd_files, self); } else { rb_raise(rb_eArgError, "Data and Filename must be passed in a hash.");} } curl_easy_setopt(curl, CURLOPT_HTTPPOST, state->post); } else { rb_raise(rb_eArgError, "Multipart PUT not supported"); } } else { rb_raise(rb_eArgError, "Must provide either data or a filename when doing a PUT or POST"); } } else if (action == rb_intern("head")) { curl_easy_setopt(curl, CURLOPT_NOBODY, 1); } else { VALUE action_name = rb_funcall(request, rb_intern("action_name"), 0); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, StringValuePtr(action_name)); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, state->headers); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, state->error_buf); url = rb_iv_get(request, "@url"); if (NIL_P(url)) { rb_raise(rb_eArgError, "Must provide a URL"); } curl_easy_setopt(curl, CURLOPT_URL, StringValuePtr(url)); timeout = rb_iv_get(request, "@timeout"); if (!NIL_P(timeout)) { curl_easy_setopt(curl, CURLOPT_TIMEOUT, FIX2INT(timeout)); } timeout = rb_iv_get(request, "@connect_timeout"); if (!NIL_P(timeout)) { curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, FIX2INT(timeout)); } redirects = rb_iv_get(request, "@max_redirects"); if (!NIL_P(redirects)) { int r = FIX2INT(redirects); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, r == 0 ? 0 : 1); curl_easy_setopt(curl, CURLOPT_MAXREDIRS, r); } proxy = rb_iv_get(request, "@proxy"); if (!NIL_P(proxy)) { curl_easy_setopt(curl, CURLOPT_PROXY, StringValuePtr(proxy)); } proxy_type = rb_iv_get(request, "@proxy_type"); if (!NIL_P(proxy_type)) { curl_easy_setopt(curl, CURLOPT_PROXYTYPE, FIX2INT(proxy_type)); } credentials = rb_funcall(request, rb_intern("credentials"), 0); if (!NIL_P(credentials)) { curl_easy_setopt(curl, CURLOPT_HTTPAUTH, FIX2INT(rb_iv_get(request, "@auth_type"))); curl_easy_setopt(curl, CURLOPT_USERPWD, StringValuePtr(credentials)); } ignore_content_length = rb_iv_get(request, "@ignore_content_length"); if (!NIL_P(ignore_content_length)) { curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, 1); } insecure = rb_iv_get(request, "@insecure"); if(!NIL_P(insecure)) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1); } buffer_size = rb_iv_get(request, "@buffer_size"); if (!NIL_P(buffer_size)) { curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, FIX2INT(buffer_size)); } if(state->debug_file) { curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); curl_easy_setopt(curl, CURLOPT_STDERR, state->debug_file); } } /* Use the info in a Curl handle to create a new Response object. */ static VALUE create_response(VALUE self, CURL* curl, VALUE header_buffer, VALUE body_buffer) { VALUE args[6] = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil }; char* effective_url = NULL; long code = 0; long count = 0; curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effective_url); args[0] = rb_str_new2(effective_url); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); args[1] = INT2NUM(code); curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &count); args[2] = INT2NUM(count); args[3] = header_buffer; args[4] = body_buffer; args[5] = rb_iv_get(self, "@default_response_charset"); return rb_class_new_instance(6, args, rb_const_get(mPatron, rb_intern("Response"))); } /* Raise an exception based on the Curl error code. */ static VALUE select_error(CURLcode code) { VALUE error = Qnil; switch (code) { case CURLE_UNSUPPORTED_PROTOCOL: error = eUnsupportedProtocol; break; case CURLE_URL_MALFORMAT: error = eURLFormatError; break; case CURLE_COULDNT_RESOLVE_HOST: error = eHostResolutionError; break; case CURLE_COULDNT_CONNECT: error = eConnectionFailed; break; case CURLE_PARTIAL_FILE: error = ePartialFileError; break; case CURLE_OPERATION_TIMEDOUT: error = eTimeoutError; break; case CURLE_TOO_MANY_REDIRECTS: error = eTooManyRedirects; break; default: error = ePatronError; } return error; } /* Perform the actual HTTP request by calling libcurl. */ static VALUE perform_request(VALUE self) { struct curl_state *state = get_curl_state(self); CURL* curl = state->handle; membuffer* header_buffer = NULL; membuffer* body_buffer = NULL; CURLcode ret = 0; state->interrupt = 0; /* clear any interrupt flags */ header_buffer = &state->header_buffer; body_buffer = &state->body_buffer; membuffer_clear(header_buffer); membuffer_clear(body_buffer); /* headers */ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, &session_write_handler); curl_easy_setopt(curl, CURLOPT_HEADERDATA, header_buffer); /* body */ if (!state->download_file) { curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &session_write_handler); curl_easy_setopt(curl, CURLOPT_WRITEDATA, body_buffer); } #if defined(HAVE_TBR) && defined(USE_TBR) ret = (CURLcode) rb_thread_blocking_region( (rb_blocking_function_t*) curl_easy_perform, curl, RUBY_UBF_IO, 0 ); #else ret = curl_easy_perform(curl); #endif if (CURLE_OK == ret) { VALUE header_str = membuffer_to_rb_str(header_buffer); VALUE body_str = Qnil; if (!state->download_file) { body_str = membuffer_to_rb_str(body_buffer); } return create_response(self, curl, header_str, body_str); } else { rb_raise(select_error(ret), "%s", state->error_buf); } } /* Cleanup after each request by resetting the Curl handle and deallocating * all request related objects such as the header slist. */ static VALUE cleanup(VALUE self) { struct curl_state *state = get_curl_state(self); curl_easy_reset(state->handle); if (state->headers) { curl_slist_free_all(state->headers); state->headers = NULL; } if (state->download_file) { fclose(state->download_file); state->download_file = NULL; } if (state->upload_file) { fclose(state->upload_file); state->upload_file = NULL; } state->upload_buf = NULL; return Qnil; } /* call-seq: * session.handle_request( request ) -> response * * Peform the actual HTTP request by calling libcurl. Each filed in the * +request+ object will be used to set the appropriate option on the libcurl * library. After the request completes, a Response object will be created and * returned. * * In the event of an error in the libcurl library, a Ruby exception will be * created and raised. The exception will return the libcurl error code and * error message. */ static VALUE session_handle_request(VALUE self, VALUE request) { set_options_from_request(self, request); return rb_ensure(&perform_request, self, &cleanup, self); } /* call-seq: * session.reset -> session * * Reset the underlying cURL session. This effectively closes all open * connections and disables debug output. */ static VALUE session_reset(VALUE self) { struct curl_state *state; Data_Get_Struct(self, struct curl_state, state); if (NULL != state->handle) { cleanup(self); curl_easy_cleanup(state->handle); state->handle = NULL; session_close_debug_file(state); } return self; } /* call-seq: * session.interrupt -> session * * Interrupt any currently executing request. This will cause the current * request to error and raise an exception. */ static VALUE session_interrupt(VALUE self) { struct curl_state *state = get_curl_state(self); state->interrupt = 1; return self; } /* call-seq: * session.enable_cookie_session( file ) -> session * * Turn on cookie handling for this session, storing them in memory by * default or in +file+ if specified. The +file+ must be readable and * writable. Calling multiple times will add more files. */ static VALUE enable_cookie_session(VALUE self, VALUE file) { struct curl_state *state = get_curl_state(self); CURL* curl = state->handle; char* file_path = NULL; file_path = RSTRING_PTR(file); if (file_path != NULL && strlen(file_path) != 0) { curl_easy_setopt(curl, CURLOPT_COOKIEJAR, file_path); } curl_easy_setopt(curl, CURLOPT_COOKIEFILE, file_path); return self; } /* call-seq: * session.set_debug_file( file ) -> session * * Enable debug output to stderr or to specified +file+. */ static VALUE set_debug_file(VALUE self, VALUE file) { struct curl_state *state = get_curl_state(self); char* file_path = RSTRING_PTR(file); session_close_debug_file(state); if(file_path != NULL && strlen(file_path) != 0) { state->debug_file = open_file(file, "w"); } else { state->debug_file = stderr; } return self; } /*----------------------------------------------------------------------------*/ /* Extension initialization */ void Init_session_ext() { curl_global_init(CURL_GLOBAL_ALL); rb_require("patron/error"); rb_set_end_proc(&cs_list_interrupt, Qnil); mPatron = rb_define_module("Patron"); ePatronError = rb_const_get(mPatron, rb_intern("Error")); eUnsupportedProtocol = rb_const_get(mPatron, rb_intern("UnsupportedProtocol")); eURLFormatError = rb_const_get(mPatron, rb_intern("URLFormatError")); eHostResolutionError = rb_const_get(mPatron, rb_intern("HostResolutionError")); eConnectionFailed = rb_const_get(mPatron, rb_intern("ConnectionFailed")); ePartialFileError = rb_const_get(mPatron, rb_intern("PartialFileError")); eTimeoutError = rb_const_get(mPatron, rb_intern("TimeoutError")); eTooManyRedirects = rb_const_get(mPatron, rb_intern("TooManyRedirects")); rb_define_module_function(mPatron, "libcurl_version", libcurl_version, 0); cSession = rb_define_class_under(mPatron, "Session", rb_cObject); cRequest = rb_define_class_under(mPatron, "Request", rb_cObject); rb_define_alloc_func(cSession, session_alloc); rb_define_method(cSession, "escape", session_escape, 1); rb_define_method(cSession, "unescape", session_unescape, 1); rb_define_method(cSession, "handle_request", session_handle_request, 1); rb_define_method(cSession, "reset", session_reset, 0); rb_define_method(cSession, "interrupt", session_interrupt, 0); rb_define_method(cSession, "enable_cookie_session", enable_cookie_session, 1); rb_define_method(cSession, "set_debug_file", set_debug_file, 1); rb_define_alias(cSession, "urlencode", "escape"); rb_define_alias(cSession, "urldecode", "unescape"); rb_define_const(cRequest, "AuthBasic", INT2FIX(CURLAUTH_BASIC)); rb_define_const(cRequest, "AuthDigest", INT2FIX(CURLAUTH_DIGEST)); rb_define_const(cRequest, "AuthAny", INT2FIX(CURLAUTH_ANY)); mProxyType = rb_define_module_under(mPatron, "ProxyType"); rb_define_const(mProxyType, "HTTP", INT2FIX(CURLPROXY_HTTP)); rb_define_const(mProxyType, "HTTP_1_0", INT2FIX(CURLPROXY_HTTP_1_0)); rb_define_const(mProxyType, "SOCKS4", INT2FIX(CURLPROXY_SOCKS4)); rb_define_const(mProxyType, "SOCKS5", INT2FIX(CURLPROXY_SOCKS5)); rb_define_const(mProxyType, "SOCKS4A", INT2FIX(CURLPROXY_SOCKS4A)); rb_define_const(mProxyType, "SOCKS5_HOSTNAME", INT2FIX(CURLPROXY_SOCKS5_HOSTNAME)); } ruby-patron-0.4.18/ext/patron/sglib.h000066400000000000000000002160621204444370700174560ustar00rootroot00000000000000/* This is SGLIB version 1.0.3 (C) by Marian Vittek, Bratislava, http://www.xref-tech.com/sglib, 2003-5 License Conditions: You can use a verbatim copy (including this copyright notice) of sglib freely in any project, commercial or not. You can also use derivative forms freely under terms of Open Source Software license or under terms of GNU Public License. If you need to use a derivative form in a commercial project, or you need sglib under any other license conditions, contact the author. */ #ifndef _SGLIB__h_ #define _SGLIB__h_ /* the assert is used exclusively to write unexpected error messages */ #include /* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */ /* - LEVEL - 0 INTERFACE - */ /* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */ /* ------------------------------ STATIC ARRAYS ------------------------------- */ /* ---------------------------------------------------------------------------- */ /* Basic algorithms for sorting arrays. Multiple depending arrays can be rearranged using user defined 'elem_exchangers' */ /* HEAP - SORT (level 0) */ #define SGLIB_ARRAY_SINGLE_HEAP_SORT(type, a, max, comparator) {\ SGLIB_ARRAY_HEAP_SORT(type, a, max, comparator, SGLIB_ARRAY_ELEMENTS_EXCHANGER);\ } #define SGLIB_ARRAY_HEAP_SORT(type, a, max, comparator, elem_exchanger) {\ int _k_;\ for(_k_=(max)/2; _k_>=0; _k_--) {\ SGLIB___ARRAY_HEAP_DOWN(type, a, _k_, max, comparator, elem_exchanger);\ }\ for(_k_=(max)-1; _k_>=0; _k_--) {\ elem_exchanger(type, a, 0, _k_);\ SGLIB___ARRAY_HEAP_DOWN(type, a, 0, _k_, comparator, elem_exchanger);\ }\ } #define SGLIB___ARRAY_HEAP_DOWN(type, a, ind, max, comparator, elem_exchanger) {\ type _t_;\ int _m_, _l_, _r_, _i_;\ _i_ = (ind);\ _m_ = _i_;\ do {\ _i_ = _m_; \ _l_ = 2*_i_+1;\ _r_ = _l_+1;\ if (_l_ < (max)){\ if (comparator(((a)[_m_]), ((a)[_l_])) < 0) _m_ = _l_;\ if (_r_ < (max)) {\ if (comparator(((a)[_m_]), ((a)[_r_])) < 0) _m_ = _r_;\ }\ }\ if (_m_ != _i_) {\ elem_exchanger(type, a, _i_, _m_);\ }\ } while (_m_ != _i_);\ } /* QUICK - SORT (level 0) */ #define SGLIB_ARRAY_SINGLE_QUICK_SORT(type, a, max, comparator) {\ SGLIB_ARRAY_QUICK_SORT(type, a, max, comparator, SGLIB_ARRAY_ELEMENTS_EXCHANGER);\ } #define SGLIB_ARRAY_QUICK_SORT(type, a, max, comparator, elem_exchanger) {\ int _i_, _j_, _p_, _stacki_, _start_, _end_;\ /* can sort up to 2^64 elements */\ int _startStack_[64]; \ int _endStack_[64];\ type _tmp_;\ _startStack_[0] = 0;\ _endStack_[0] = (max);\ _stacki_ = 1;\ while (_stacki_ > 0) {\ _stacki_ --;\ _start_ = _startStack_[_stacki_];\ _end_ = _endStack_[_stacki_];\ while (_end_ - _start_ > 2) {\ _p_ = _start_;\ _i_ = _start_ + 1;\ _j_ = _end_ - 1;\ while (_i_<_j_) {\ for(; _i_<=_j_ && comparator(((a)[_i_]),((a)[_p_]))<=0; _i_++) ;\ if (_i_ > _j_) {\ /* all remaining elements lesseq than pivot */\ elem_exchanger(type, a, _j_, _p_);\ _i_ = _j_;\ } else {\ for(; _i_<=_j_ && comparator(((a)[_j_]),((a)[_p_]))>=0; _j_--) ;\ if (_i_ > _j_) {\ /* all remaining elements greater than pivot */\ elem_exchanger(type, a, _j_, _p_);\ _i_ = _j_;\ } else if (_i_ < _j_) {\ elem_exchanger(type, a, _i_, _j_);\ if (_i_+2 < _j_) {_i_++; _j_--;}\ else if (_i_+1 < _j_) _i_++;\ }\ }\ }\ /* O.K. i==j and pivot is on a[i] == a[j] */\ /* handle recursive calls without recursion */\ if (_i_-_start_ > 1 && _end_-_j_ > 1) {\ /* two recursive calls, use array-stack */\ if (_i_-_start_ < _end_-_j_-1) {\ _startStack_[_stacki_] = _j_+1;\ _endStack_[_stacki_] = _end_;\ _stacki_ ++;\ _end_ = _i_;\ } else {\ _startStack_[_stacki_] = _start_;\ _endStack_[_stacki_] = _i_;\ _stacki_ ++;\ _start_ = _j_+1;\ }\ } else {\ if (_i_-_start_ > 1) {\ _end_ = _i_;\ } else {\ _start_ = _j_+1;\ }\ }\ }\ if (_end_ - _start_ == 2) {\ if (comparator(((a)[_start_]),((a)[_end_-1])) > 0) {\ elem_exchanger(type, a, _start_, _end_-1);\ }\ }\ }\ } /* BINARY SEARCH (level 0) */ #define SGLIB_ARRAY_BINARY_SEARCH(type, a, start_index, end_index, key, comparator, found, result_index) {\ int _kk_, _cc_, _ii_, _jj_, _ff_;\ _ii_ = (start_index); \ _jj_ = (end_index);\ _ff_ = 0;\ while (_ii_ <= _jj_ && _ff_==0) {\ _kk_ = (_jj_+_ii_)/2;\ _cc_ = comparator(((a)[_kk_]), (key));\ if (_cc_ == 0) {\ (result_index) = _kk_; \ _ff_ = 1;\ } else if (_cc_ < 0) {\ _ii_ = _kk_+1;\ } else {\ _jj_ = _kk_-1;\ }\ }\ if (_ff_ == 0) {\ /* not found, but set its resulting place in the array */\ (result_index) = _jj_+1;\ }\ (found) = _ff_;\ } /* -------------------------------- queue (in an array) ------------------ */ /* queue is a quadruple (a,i,j,dim) such that: */ /* a is the array storing values */ /* i is the index of the first used element in the array */ /* j is the index of the first free element in the array */ /* dim is the size of the array a */ /* !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! */ #define SGLIB_QUEUE_INIT(type, a, i, j) { i = j = 0; } #define SGLIB_QUEUE_IS_EMPTY(type, a, i, j) ((i)==(j)) #define SGLIB_QUEUE_IS_FULL(type, a, i, j, dim) ((i)==((j)+1)%(dim)) #define SGLIB_QUEUE_FIRST_ELEMENT(type, a, i, j) (a[i]) #define SGLIB_QUEUE_ADD_NEXT(type, a, i, j, dim) {\ if (SGLIB_QUEUE_IS_FULL(type, a, i, j, dim)) assert(0 && "the queue is full");\ (j) = ((j)+1) % (dim);\ } #define SGLIB_QUEUE_ADD(type, a, elem, i, j, dim) {\ a[j] = (elem);\ SGLIB_QUEUE_ADD_NEXT(type, a, i, j, dim);\ } #define SGLIB_QUEUE_DELETE_FIRST(type, a, i, j, dim) {\ if (SGLIB_QUEUE_IS_EMPTY(type, a, i, j)) assert(0 && "the queue is empty");\ (i) = ((i)+1) % (dim);\ } #define SGLIB_QUEUE_DELETE(type, a, i, j, dim) {\ SGLIB_QUEUE_DELETE_FIRST(type, a, i, j, dim);\ } /* ----------------- priority queue (heap) (in an array) -------------------- */ /* heap is a triple (a,i,dim) such that: */ /* a is the array storing values */ /* i is the index of the first free element in the array */ /* dim is the size of the array a */ /* !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! */ #define SGLIB_HEAP_INIT(type, a, i) { i = 0; } #define SGLIB_HEAP_IS_EMPTY(type, a, i) ((i)==0) #define SGLIB_HEAP_IS_FULL(type, a, i, dim) ((i)==(dim)) #define SGLIB_HEAP_FIRST_ELEMENT(type, a, i) (a[0]) #define SGLIB_HEAP_ADD_NEXT(type, a, i, dim, comparator, elem_exchanger) {\ int _i_;\ if (SGLIB_HEAP_IS_FULL(type, a, i, dim)) assert(0 && "the heap is full");\ _i_ = (i)++;\ while (_i_ > 0 && comparator(a[_i_/2], a[_i_]) < 0) {\ elem_exchanger(type, a, (_i_/2), _i_);\ _i_ = _i_/2;\ }\ } #define SGLIB_HEAP_ADD(type, a, elem, i, dim, comparator) {\ if (SGLIB_HEAP_IS_FULL(type, a, i, dim)) assert(0 && "the heap is full");\ a[i] = (elem);\ SGLIB_HEAP_ADD_NEXT(type, a, i, dim, comparator, SGLIB_ARRAY_ELEMENTS_EXCHANGER);\ } #define SGLIB_HEAP_DELETE_FIRST(type, a, i, dim, comparator, elem_exchanger) {\ if (SGLIB_HEAP_IS_EMPTY(type, a, i)) assert(0 && "the heap is empty");\ (i)--;\ a[0] = a[i];\ SGLIB___ARRAY_HEAP_DOWN(type, a, 0, i, comparator, elem_exchanger);\ } #define SGLIB_HEAP_DELETE(type, a, i, dim, comparator) {\ SGLIB_HEAP_DELETE_FIRST(type, a, i, dim, comparator, SGLIB_ARRAY_ELEMENTS_EXCHANGER);\ } /* ----------------- hashed table of pointers (in an array) -------------------- */ /* This hashed table is storing pointers to objects (not containers). In this table there is a one-to-one mapping between 'objects' stored in the table and indexes where they are placed. Each index is pointing to exactly one 'object' and each 'object' stored in the table occurs on exactly one index. Once an object is stored in the table, it can be represented via its index. In case of collision while adding an object the index shifted by SGLIB_HASH_TAB_SHIFT_CONSTANT (constant can be redefined) You can NOT delete an element from such hash table. The only justification (I can see) for this data structure is an exchange file format, having an index table at the beginning and then refering objects via indexes. !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! */ #define SGLIB_HASH_TAB_INIT(type, table, dim) {\ int _i_;\ for(_i_ = 0; _i_ < (dim); _i_++) (table)[_i_] = NULL;\ } #define SGLIB_HASH_TAB_ADD_IF_NOT_MEMBER(type, table, dim, elem, hash_function, comparator, member){\ unsigned _pos_;\ type *_elem_;\ SGLIB_HASH_TAB_FIND_MEMBER(type, table, dim, elem, _pos_, _elem_);\ (member) = (table)[_pos_];\ if (_elem_ == NULL) {\ if ((table)[_pos_] != NULL) assert(0 && "the hash table is full");\ (table)[_pos_] = (elem);\ }\ } #define SGLIB_HASH_TAB_FIND_MEMBER(type, table, dim, elem, hash_function, comparator, resultIndex, resultMember) {\ unsigned _i_;\ int _count_;\ type *_e_;\ _count = 0;\ _i_ = hash_function(elem);\ _i_ %= (dim);\ while ((_e_=(table)[_i_])!=NULL && comparator(_e_, (elem))!=0 && _count_<(dim)) {\ _count_ ++;\ _i_ = (_i_ + SGLIB_HASH_TAB_SHIFT_CONSTANT) % (dim);\ }\ (resultIndex) = _i_;\ if (_count_ < (dim)) (resultMember) = _e_;\ else (resultMember) = NULL;\ } #define SGLIB_HASH_TAB_IS_MEMBER(type, table, dim, elem, hash_function, resultIndex) {\ unsigned _i_;\ int _c_;\ type *_e_;\ _count = 0;\ _i_ = hash_function(elem);\ _i_ %= (dim);\ while ((_e_=(table)[_i_])!=NULL && _e_!=(elem) && _c_<(dim)) {\ _c_ ++;\ _i_ = (_i_ + SGLIB_HASH_TAB_SHIFT_CONSTANT) % (dim);\ }\ if (_e_==(elem)) (resultIndex) = _i_;\ else (resultIndex) = -1;\ } #define SGLIB_HASH_TAB_MAP_ON_ELEMENTS(type, table, dim, iteratedIndex, iteratedVariable, command) {\ unsigned iteratedIndex;\ type *iteratedVariable;\ for(iteratedIndex=0; iteratedIndex < (dim); iteratedIndex++) {\ iteratedVariable = (table)[iteratedIndex];\ if (iteratedVariable != NULL) {command;}\ }\ } /* ---------------------------------------------------------------------------- */ /* ------------------------- DYNAMIC DATA STRUCTURES -------------------------- */ /* ---------------------------------------------------------------------------- */ /* ------------------------------------ lists (level 0) --------------------- */ #define SGLIB_LIST_ADD(type, list, elem, next) {\ (elem)->next = (list);\ (list) = (elem);\ } #define SGLIB_LIST_CONCAT(type, first, second, next) {\ if ((first)==NULL) {\ (first) = (second);\ } else {\ type *_p_;\ for(_p_ = (first); _p_->next!=NULL; _p_=_p_->next) ;\ _p_->next = (second);\ }\ } #define SGLIB_LIST_DELETE(type, list, elem, next) {\ type **_p_;\ for(_p_ = &(list); *_p_!=NULL && *_p_!=(elem); _p_= &(*_p_)->next) ;\ assert(*_p_!=NULL && "element is not member of the container, use DELETE_IF_MEMBER instead"!=NULL);\ *_p_ = (*_p_)->next;\ } #define SGLIB_LIST_ADD_IF_NOT_MEMBER(type, list, elem, comparator, next, member) {\ type *_p_;\ for(_p_ = (list); _p_!=NULL && comparator(_p_, (elem)) != 0; _p_= _p_->next) ;\ (member) = _p_;\ if (_p_ == NULL) {\ SGLIB_LIST_ADD(type, list, elem, next);\ }\ } #define SGLIB_LIST_DELETE_IF_MEMBER(type, list, elem, comparator, next, member) {\ type **_p_;\ for(_p_ = &(list); *_p_!=NULL && comparator((*_p_), (elem)) != 0; _p_= &(*_p_)->next) ;\ (member) = *_p_;\ if (*_p_ != NULL) {\ *_p_ = (*_p_)->next;\ }\ } #define SGLIB_LIST_IS_MEMBER(type, list, elem, next, result) {\ type *_p_;\ for(_p_ = (list); _p_!=NULL && _p_ != (elem); _p_= _p_->next) ;\ (result) = (_p_!=NULL);\ } #define SGLIB_LIST_FIND_MEMBER(type, list, elem, comparator, next, member) {\ type *_p_;\ for(_p_ = (list); _p_!=NULL && comparator(_p_, (elem)) != 0; _p_= _p_->next) ;\ (member) = _p_;\ } #define SGLIB_LIST_MAP_ON_ELEMENTS(type, list, iteratedVariable, next, command) {\ type *_ne_;\ type *iteratedVariable;\ (iteratedVariable) = (list); \ while ((iteratedVariable)!=NULL) {\ _ne_ = (iteratedVariable)->next;\ {command;};\ (iteratedVariable) = _ne_;\ }\ } #define SGLIB_LIST_LEN(type, list, next, result) {\ type *_ce_;\ (result) = 0;\ SGLIB_LIST_MAP_ON_ELEMENTS(type, list, _ce_, next, (result)++);\ } #define SGLIB_LIST_REVERSE(type, list, next) {\ type *_list_,*_tmp_,*_res_;\ _list_ = (list);\ _res_ = NULL;\ while (_list_!=NULL) {\ _tmp_ = _list_->next; _list_->next = _res_;\ _res_ = _list_; _list_ = _tmp_;\ }\ (list) = _res_;\ } #define SGLIB_LIST_SORT(type, list, comparator, next) {\ /* a non-recursive merge sort on lists */\ type *_r_;\ type *_a_, *_b_, *_todo_, *_t_, **_restail_;\ int _i_, _n_, _contFlag_;\ _r_ = (list);\ _contFlag_ = 1;\ for(_n_ = 1; _contFlag_; _n_ = _n_+_n_) {\ _todo_ = _r_; _r_ = NULL; _restail_ = &_r_; _contFlag_ =0;\ while (_todo_!=NULL) {\ _a_ = _todo_;\ for(_i_ = 1, _t_ = _a_; _i_ < _n_ && _t_!=NULL; _i_++, _t_ = _t_->next) ;\ if (_t_ ==NULL) {\ *_restail_ = _a_;\ break;\ }\ _b_ = _t_->next; _t_->next=NULL;\ for(_i_ =1, _t_ = _b_; _i_<_n_ && _t_!=NULL; _i_++, _t_ = _t_->next) ;\ if (_t_ ==NULL) {\ _todo_ =NULL;\ } else {\ _todo_ = _t_->next; _t_->next=NULL;\ }\ /* merge */\ while (_a_!=NULL && _b_!=NULL) {\ if (comparator(_a_, _b_) < 0) {\ *_restail_ = _a_; _restail_ = &(_a_->next); _a_ = _a_->next;\ } else {\ *_restail_ = _b_; _restail_ = &(_b_->next); _b_ = _b_->next;\ }\ }\ if (_a_!=NULL) *_restail_ = _a_;\ else *_restail_ = _b_;\ while (*_restail_!=NULL) _restail_ = &((*_restail_)->next);\ _contFlag_ =1;\ }\ }\ (list) = _r_;\ } /* --------------------------------- sorted list (level 0) --------------------- */ /* All operations suppose that the list is sorted and they preserve this property. */ #define SGLIB_SORTED_LIST_ADD(type, list, elem, comparator, next) {\ type **_e_;\ int _cmpres_;\ SGLIB_SORTED_LIST_FIND_MEMBER_OR_PLACE(type, list, elem, comparator, next, _cmpres_, _e_);\ (elem)->next = *_e_;\ *_e_ = (elem);\ } #define SGLIB_SORTED_LIST_ADD_IF_NOT_MEMBER(type, list, elem, comparator, next, member) {\ type **_e_;\ int _cmp_res_;\ SGLIB_SORTED_LIST_FIND_MEMBER_OR_PLACE(type, list, elem, comparator, next, _cmp_res_, _e_);\ if (_cmp_res_ != 0) {\ (elem)->next = *_e_;\ *_e_ = (elem);\ (member) = NULL;\ } else {\ (member) = *_e_;\ }\ } #define SGLIB_SORTED_LIST_DELETE(type, list, elem, next) {\ SGLIB_LIST_DELETE(type, list, elem, next);\ } #define SGLIB_SORTED_LIST_DELETE_IF_MEMBER(type, list, elem, comparator, next, member) {\ type **_e_;\ int _cmp_res_;\ SGLIB_SORTED_LIST_FIND_MEMBER_OR_PLACE(type, list, elem, comparator, next, _cmp_res_, _e_);\ if (_cmp_res_ == 0) {\ (member) = *_e_;\ *_e_ = (*_e_)->next;\ } else {\ (member) = NULL;\ }\ } #define SGLIB_SORTED_LIST_FIND_MEMBER(type, list, elem, comparator, next, member) {\ type *_p_;\ int _cmpres_ = 1;\ for(_p_ = (list); _p_!=NULL && (_cmpres_=comparator(_p_, (elem))) < 0; _p_=_p_->next) ;\ if (_cmpres_ != 0) (member) = NULL;\ else (member) = _p_;\ } #define SGLIB_SORTED_LIST_IS_MEMBER(type, list, elem, comparator, next, result) {\ type *_p_;\ for(_p_ = (list); _p_!=NULL && comparator(_p_, (elem)) < 0; _p_=_p_->next) ;\ while (_p_ != NULL && _p_ != (elem) && comparator(_p_, (elem)) == 0) _p_=_p_->next;\ (result) = (_p_ == (elem));\ } #define SGLIB_SORTED_LIST_FIND_MEMBER_OR_PLACE(type, list, elem, comparator, next, comparator_result, member_ptr) {\ (comparator_result) = -1;\ for((member_ptr) = &(list); \ *(member_ptr)!=NULL && ((comparator_result)=comparator((*member_ptr), (elem))) < 0; \ (member_ptr) = &(*(member_ptr))->next) ;\ } #define SGLIB_SORTED_LIST_LEN(type, list, next, result) {\ SGLIB_LIST_LEN(type, list, next, result);\ } #define SGLIB_SORTED_LIST_MAP_ON_ELEMENTS(type, list, iteratedVariable, next, command) {\ SGLIB_LIST_MAP_ON_ELEMENTS(type, list, iteratedVariable, next, command);\ } /* ------------------------------- double linked list (level 0) ------------------------- */ /* Lists with back pointer to previous element. Those lists implements deletion of an element in a constant time. */ #define SGLIB___DL_LIST_CREATE_SINGLETON(type, list, elem, previous, next) {\ (list) = (elem);\ (list)->next = (list)->previous = NULL;\ } #define SGLIB_DL_LIST_ADD_AFTER(type, place, elem, previous, next) {\ if ((place) == NULL) {\ SGLIB___DL_LIST_CREATE_SINGLETON(type, place, elem, previous, next);\ } else {\ (elem)->next = (place)->next;\ (elem)->previous = (place);\ (place)->next = (elem);\ if ((elem)->next != NULL) (elem)->next->previous = (elem);\ }\ } #define SGLIB_DL_LIST_ADD_BEFORE(type, place, elem, previous, next) {\ if ((place) == NULL) {\ SGLIB___DL_LIST_CREATE_SINGLETON(type, place, elem, previous, next);\ } else {\ (elem)->next = (place);\ (elem)->previous = (place)->previous;\ (place)->previous = (elem);\ if ((elem)->previous != NULL) (elem)->previous->next = (elem);\ }\ } #define SGLIB_DL_LIST_ADD(type, list, elem, previous, next) {\ SGLIB_DL_LIST_ADD_BEFORE(type, list, elem, previous, next)\ } #define SGLIB___DL_LIST_GENERIC_ADD_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member, the_add_operation) {\ type *_dlp_;\ for(_dlp_ = (list); _dlp_!=NULL && comparator(_dlp_, (elem)) != 0; _dlp_= _dlp_->previous) ;\ if (_dlp_ == NULL && (list) != NULL) {\ for(_dlp_ = (list)->next; _dlp_!=NULL && comparator(_dlp_, (elem)) != 0; _dlp_= _dlp_->next) ;\ }\ (member) = _dlp_;\ if (_dlp_ == NULL) {\ the_add_operation(type, list, elem, previous, next);\ }\ } #define SGLIB_DL_LIST_ADD_BEFORE_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member) {\ SGLIB___DL_LIST_GENERIC_ADD_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member, SGLIB_DL_LIST_ADD_BEFORE);\ } #define SGLIB_DL_LIST_ADD_AFTER_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member) {\ SGLIB___DL_LIST_GENERIC_ADD_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member, SGLIB_DL_LIST_ADD_AFTER);\ } #define SGLIB_DL_LIST_ADD_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member) {\ SGLIB___DL_LIST_GENERIC_ADD_IF_NOT_MEMBER(type, list, elem, comparator, previous, next, member, SGLIB_DL_LIST_ADD);\ } #define SGLIB_DL_LIST_CONCAT(type, first, second, previous, next) {\ if ((first)==NULL) {\ (first) = (second);\ } else if ((second)!=NULL) {\ type *_dlp_;\ for(_dlp_ = (first); _dlp_->next!=NULL; _dlp_=_dlp_->next) ;\ SGLIB_DL_LIST_ADD_AFTER(type, _dlp_, second, previous, next);\ }\ } #define SGLIB_DL_LIST_DELETE(type, list, elem, previous, next) {\ type *_l_;\ _l_ = (list);\ if (_l_ == (elem)) {\ if ((elem)->previous != NULL) _l_ = (elem)->previous;\ else _l_ = (elem)->next;\ }\ if ((elem)->next != NULL) (elem)->next->previous = (elem)->previous;\ if ((elem)->previous != NULL) (elem)->previous->next = (elem)->next;\ (list) = _l_;\ } #define SGLIB_DL_LIST_DELETE_IF_MEMBER(type, list, elem, comparator, previous, next, member) {\ type *_dlp_;\ for(_dlp_ = (list); _dlp_!=NULL && comparator(_dlp_, (elem)) != 0; _dlp_= _dlp_->previous) ;\ if (_dlp_ == NULL && (list) != NULL) {\ for(_dlp_ = (list)->next; _dlp_!=NULL && comparator(_dlp_, (elem)) != 0; _dlp_= _dlp_->next) ;\ }\ (member) = _dlp_;\ if (_dlp_ != NULL) {\ SGLIB_DL_LIST_DELETE(type, list, _dlp_, previous, next);\ }\ } #define SGLIB_DL_LIST_IS_MEMBER(type, list, elem, previous, next, result) {\ type *_dlp_;\ SGLIB_LIST_IS_MEMBER(type, list, elem, previous, result);\ if (result == 0 && (list) != NULL) {\ _dlp_ = (list)->next;\ SGLIB_LIST_IS_MEMBER(type, _dlp_, elem, next, result);\ }\ } #define SGLIB_DL_LIST_FIND_MEMBER(type, list, elem, comparator, previous, next, member) {\ type *_dlp_;\ SGLIB_LIST_FIND_MEMBER(type, list, elem, comparator, previous, member);\ if ((member) == NULL && (list) != NULL) {\ _dlp_ = (list)->next;\ SGLIB_LIST_FIND_MEMBER(type, _dlp_, elem, comparator, next, member);\ }\ } #define SGLIB_DL_LIST_MAP_ON_ELEMENTS(type, list, iteratedVariable, previous, next, command) {\ type *_dl_;\ type *iteratedVariable;\ if ((list)!=NULL) {\ _dl_ = (list)->next;\ SGLIB_LIST_MAP_ON_ELEMENTS(type, list, iteratedVariable, previous, command);\ SGLIB_LIST_MAP_ON_ELEMENTS(type, _dl_, iteratedVariable, next, command);\ }\ } #define SGLIB_DL_LIST_SORT(type, list, comparator, previous, next) {\ type *_dll_, *_dlp_, *_dlt_;\ _dll_ = (list);\ if (_dll_ != NULL) {\ for(; _dll_->previous!=NULL; _dll_=_dll_->previous) ;\ SGLIB_LIST_SORT(type, _dll_, comparator, next);\ SGLIB___DL_LIST_CREATE_FROM_LIST(type, _dll_, previous, next);\ (list) = _dll_;\ }\ } #define SGLIB_DL_LIST_GET_FIRST(type, list, previous, next, result) {\ type *_dll_;\ _dll_ = (list);\ if (_dll_ != NULL) {\ for(; _dll_->previous!=NULL; _dll_=_dll_->previous) ;\ }\ (result) = _dll_;\ } #define SGLIB_DL_LIST_GET_LAST(type, list, previous, next, result) {\ type *_dll_;\ _dll_ = (list);\ if (_dll_ != NULL) {\ for(; _dll_->next!=NULL; _dll_=_dll_->next) ;\ }\ (result) = _dll_;\ } #define SGLIB_DL_LIST_LEN(type, list, previous, next, result) {\ type *_dl_;\ int _r1_, _r2_;\ if ((list)==NULL) {\ (result) = 0;\ } else {\ SGLIB_LIST_LEN(type, list, previous, _r1_);\ _dl_ = (list)->next;\ SGLIB_LIST_LEN(type, _dl_, next, _r2_);\ (result) = _r1_ + _r2_;\ }\ } #define SGLIB_DL_LIST_REVERSE(type, list, previous, next) {\ type *_list_,*_nlist_,*_dlp_,*_dln_;\ _list_ = (list);\ if (_list_!=NULL) {\ _nlist_ = _list_->next;\ while (_list_!=NULL) {\ _dln_ = _list_->next; \ _dlp_ = _list_->previous; \ _list_->next = _dlp_;\ _list_->previous = _dln_;\ _list_ = _dlp_;\ }\ while (_nlist_!=NULL) {\ _dln_ = _nlist_->next; \ _dlp_ = _nlist_->previous; \ _nlist_->next = _dlp_;\ _nlist_->previous = _dln_;\ _nlist_ = _dln_;\ }\ }\ } #define SGLIB___DL_LIST_CREATE_FROM_LIST(type, list, previous, next) {\ type *_dlp_, *_dlt_;\ _dlp_ = NULL;\ for(_dlt_ = (list); _dlt_!=NULL; _dlt_ = _dlt_->next) {\ _dlt_->previous = _dlp_;\ _dlp_ = _dlt_;\ }\ } /* ------------------------------- binary tree traversal (level 0) -------------------- */ #define SGLIB___BIN_TREE_MAP_ON_ELEMENTS(type, tree, iteratedVariable, order, left, right, command) {\ /* this is non-recursive implementation of tree traversal */\ /* it maintains the path to the current node in the array '_path_' */\ /* the _path_[0] contains the root of the tree; */\ /* the _path_[_pathi_] contains the _current_element_ */\ /* the macro does not use the _current_element_ after execution of command */\ /* command can destroy it, it can free the element for example */\ type *_path_[SGLIB_MAX_TREE_DEEP];\ type *_right_[SGLIB_MAX_TREE_DEEP];\ char _pass_[SGLIB_MAX_TREE_DEEP];\ type *_cn_;\ int _pathi_;\ type *iteratedVariable;\ _cn_ = (tree);\ _pathi_ = 0;\ while (_cn_!=NULL) {\ /* push down to leftmost innermost element */\ while(_cn_!=NULL) {\ _path_[_pathi_] = _cn_;\ _right_[_pathi_] = _cn_->right;\ _pass_[_pathi_] = 0;\ _cn_ = _cn_->left;\ if (order == 0) {\ iteratedVariable = _path_[_pathi_];\ {command;}\ }\ _pathi_ ++;\ if (_pathi_ >= SGLIB_MAX_TREE_DEEP) assert(0 && "the binary_tree is too deep");\ }\ do {\ _pathi_ --;\ if ((order==1 && _pass_[_pathi_] == 0)\ || (order == 2 && (_pass_[_pathi_] == 1 || _right_[_pathi_]==NULL))) {\ iteratedVariable = _path_[_pathi_];\ {command;}\ }\ _pass_[_pathi_] ++;\ } while (_pathi_>0 && _right_[_pathi_]==NULL) ;\ _cn_ = _right_[_pathi_];\ _right_[_pathi_] = NULL;\ _pathi_ ++;\ }\ } #define SGLIB_BIN_TREE_MAP_ON_ELEMENTS(type, tree, _current_element_, left, right, command) {\ SGLIB___BIN_TREE_MAP_ON_ELEMENTS(type, tree, _current_element_, 1, left, right, command);\ } #define SGLIB_BIN_TREE_MAP_ON_ELEMENTS_PREORDER(type, tree, _current_element_, left, right, command) {\ SGLIB___BIN_TREE_MAP_ON_ELEMENTS(type, tree, _current_element_, 0, left, right, command);\ } #define SGLIB_BIN_TREE_MAP_ON_ELEMENTS_POSTORDER(type, tree, _current_element_, left, right, command) {\ SGLIB___BIN_TREE_MAP_ON_ELEMENTS(type, tree, _current_element_, 2, left, right, command);\ } #define SGLIB___BIN_TREE_FIND_MEMBER(type, tree, elem, left, right, comparator, res) {\ type *_s_;\ int _c_;\ _s_ = (tree);\ while (_s_!=NULL) {\ _c_ = comparator((elem), _s_);\ if (_c_ < 0) _s_ = _s_->left;\ else if (_c_ > 0) _s_ = _s_->right;\ else break;\ }\ (res) = _s_;\ } /* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */ /* - LEVEL - 1 INTERFACE - */ /* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */ /* ------------------------------ STATIC ARRAYS ------------------------------- */ /* ---------------------------------------------------------------------------- */ /* ----------------------------- array sorting (level 1) ---------------------- */ #define SGLIB_DEFINE_ARRAY_SORTING_PROTOTYPES(type, comparator) \ extern void sglib_##type##_array_quick_sort(type *a, int max);\ extern void sglib_##type##_array_heap_sort(type *a, int max);\ #define SGLIB_DEFINE_ARRAY_SORTING_FUNCTIONS(type, comparator) \ void sglib_##type##_array_quick_sort(type *a, int max) {\ SGLIB_ARRAY_SINGLE_QUICK_SORT(type, a, max, comparator);\ }\ void sglib_##type##_array_heap_sort(type *a, int max) {\ SGLIB_ARRAY_SINGLE_HEAP_SORT(type, a, max, comparator);\ }\ /* ----------------------------- array queue (level 1) ------------------- */ /* sglib's queue is stored in a fixed sized array */ /* queue_type MUST be a structure containing fields: */ /* afield is the array storing elem_type */ /* ifield is the index of the first element in the queue */ /* jfield is the index of the first free element after the queue */ /* dim is the size of the array afield */ /* !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! */ #define SGLIB_DEFINE_QUEUE_PROTOTYPES(queue_type, elem_type, afield, ifield, jfield, dim) \ extern void sglib_##queue_type##_init(queue_type *q); \ extern int sglib_##queue_type##_is_empty(queue_type *q); \ extern int sglib_##queue_type##_is_full(queue_type *q); \ extern elem_type sglib_##queue_type##_first_element(queue_type *q); \ extern elem_type *sglib_##queue_type##_first_element_ptr(queue_type *q); \ extern void sglib_##queue_type##_add_next(queue_type *q); \ extern void sglib_##queue_type##_add(queue_type *q, elem_type elem); \ extern void sglib_##queue_type##_delete_first(queue_type *q); \ extern void sglib_##queue_type##_delete(queue_type *q); #define SGLIB_DEFINE_QUEUE_FUNCTIONS(queue_type, elem_type, afield, ifield, jfield, dim) \ void sglib_##queue_type##_init(queue_type *q) {\ SGLIB_QUEUE_INIT(elem_type, q->afield, q->ifield, q->jfield);\ }\ int sglib_##queue_type##_is_empty(queue_type *q) {\ return(SGLIB_QUEUE_IS_EMPTY(elem_type, q->afield, q->ifield, q->jfield));\ }\ int sglib_##queue_type##_is_full(queue_type *q) {\ return(SGLIB_QUEUE_IS_FULL(elem_type, q->afield, q->ifield, q->jfield));\ }\ elem_type sglib_##queue_type##_first_element(queue_type *q) {\ return(SGLIB_QUEUE_FIRST_ELEMENT(elem_type, q->afield, q->ifield, q->jfield));\ }\ elem_type *sglib_##queue_type##_first_element_ptr(queue_type *q) {\ return(& SGLIB_QUEUE_FIRST_ELEMENT(elem_type, q->afield, q->ifield, q->jfield));\ }\ void sglib_##queue_type##_add_next(queue_type *q) {\ SGLIB_QUEUE_ADD_NEXT(elem_type, q->afield, q->ifield, q->jfield, dim);\ }\ void sglib_##queue_type##_add(queue_type *q, elem_type elem) {\ SGLIB_QUEUE_ADD(elem_type, q->afield, elem, q->ifield, q->jfield, dim);\ }\ void sglib_##queue_type##_delete_first(queue_type *q) {\ SGLIB_QUEUE_DELETE_FIRST(elem_type, q->afield, q->ifield, q->jfield, dim);\ }\ void sglib_##queue_type##_delete(queue_type *q) {\ SGLIB_QUEUE_DELETE_FIRST(elem_type, q->afield, q->ifield, q->jfield, dim);\ } /* ------------------------ array heap (level 1) ------------------------- */ /* sglib's heap is a priority queue implemented in a fixed sized array */ /* heap_type MUST be a structure containing fields: */ /* afield is the array of size dim storing elem_type */ /* ifield is the index of the first free element after the queue */ /* !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! */ #define SGLIB_DEFINE_HEAP_PROTOTYPES(heap_type, elem_type, afield, ifield, dim, comparator, elem_exchanger) \ extern void sglib_##heap_type##_init(heap_type *q); \ extern int sglib_##heap_type##_is_empty(heap_type *q); \ extern int sglib_##heap_type##_is_full(heap_type *q); \ extern elem_type sglib_##heap_type##_first_element(heap_type *q); \ extern elem_type *sglib_##heap_type##_first_element_ptr(heap_type *q); \ extern void sglib_##heap_type##_add_next(heap_type *q); \ extern void sglib_##heap_type##_add(heap_type *q, elem_type elem); \ extern void sglib_##heap_type##_delete_first(heap_type *q); \ extern void sglib_##heap_type##_delete(heap_type *q) #define SGLIB_DEFINE_HEAP_FUNCTIONS(heap_type, elem_type, afield, ifield, dim, comparator, elem_exchanger) \ void sglib_##heap_type##_init(heap_type *q) {\ SGLIB_HEAP_INIT(elem_type, q->afield, q->ifield);\ }\ int sglib_##heap_type##_is_empty(heap_type *q) {\ return(SGLIB_HEAP_IS_EMPTY(elem_type, q->afield, q->ifield));\ }\ int sglib_##heap_type##_is_full(heap_type *q) {\ return(SGLIB_HEAP_IS_FULL(elem_type, q->afield, q->ifield));\ }\ elem_type sglib_##heap_type##_first_element(heap_type *q) {\ return(SGLIB_HEAP_FIRST_ELEMENT(elem_type, q->afield, q->ifield));\ }\ elem_type *sglib_##heap_type##_first_element_ptr(heap_type *q) {\ return(& SGLIB_HEAP_FIRST_ELEMENT(elem_type, q->afield, q->ifield));\ }\ void sglib_##heap_type##_add_next(heap_type *q) {\ SGLIB_HEAP_ADD_NEXT(elem_type, q->afield, q->ifield, dim, comparator, elem_exchanger);\ }\ void sglib_##heap_type##_add(heap_type *q, elem_type elem) {\ SGLIB_HEAP_ADD(elem_type, q->afield, elem, q->ifield, dim, comparator, elem_exchanger);\ }\ void sglib_##heap_type##_delete_first(heap_type *q) {\ SGLIB_HEAP_DELETE_FIRST(elem_type, q->afield, q->ifield, dim, comparator, elem_exchanger);\ }\ void sglib_##heap_type##_delete(heap_type *q) {\ SGLIB_HEAP_DELETE_FIRST(elem_type, q->afield, q->ifield, dim, comparator, elem_exchanger);\ } /* ------------------------ hashed table (level 1) ------------------------- */ /* sglib's hash table is an array storing directly pointers to objects (not containers). In this table there is a one-to-one mapping between 'objects' stored in the table and indexes where they are placed. Each index is pointing to exactly one 'object' and each 'object' stored in the table occurs on exactly one index. Once an object is stored in the table, it can be represented via its index. type - is the type of elements dim - is the size of the hash array hash_function - is a hashing function mapping type* to unsigned comparator - is a comparator on elements !!!!!!! This data structure is NOT documented, do not use it !!!!!!!!!! */ #define SGLIB_DEFINE_HASHED_TABLE_PROTOTYPES(type, dim, hash_function, comparator) \ struct sglib_hashed_##type##_iterator {\ int currentIndex;\ int (*subcomparator)(type *, type *);\ type *equalto;\ };\ extern void sglib_hashed_##type##_init(type *table[dim]);\ extern int sglib_hashed_##type##_add_if_not_member(type *table[dim], type *elem, type **member);\ extern int sglib_hashed_##type##_is_member(type *table[dim], type *elem);\ extern type * sglib_hashed_##type##_find_member(type *table[dim], type *elem);\ extern type *sglib_hashed_##type##_it_init(struct sglib_hashed_##type##_iterator *it, type *table[dim]); \ extern type *sglib_hashed_##type##_it_init_on_equal(struct sglib_hashed_##type##_iterator *it, type *table[dim], int (*subcomparator)(type *, type *), type *equalto); \ extern type *sglib_hashed_##type##_it_current(struct sglib_hashed_##type##_iterator *it); \ extern type *sglib_hashed_##type##_it_next(struct sglib_hashed_##type##_iterator *it); #define SGLIB_DEFINE_HASHED_TABLE_FUNCTIONS(type, dim, hash_function, comparator) \ struct sglib_hashed_##type##_iterator {\ int currentIndex;\ type **table;\ int (*subcomparator)(type *, type *);\ type *equalto;\ };\ void sglib_hashed_##type##_init(type *table[dim]) {\ SGLIB_HASH_TAB_INIT(type, table, dim);\ }\ int sglib_hashed_##type##_add_if_not_member(type *table[dim], type *elem, type **member) {\ SGLIB_HASH_TAB_ADD_IF_NOT_MEMBER(type, table, dim, elem, hash_function, comparator, *member);\ }\ int sglib_hashed_##type##_is_member(type *table[dim], type *elem) {\ int ind;\ SGLIB_HASH_TAB_IS_MEMBER(type, table, dim, elem, hash_function, ind);\ return(ind != -1);\ }\ type * sglib_hashed_##type##_find_member(type *table[dim], type *elem) {\ type *mmb;\ int ind;\ SGLIB_HASH_TAB_FIND_MEMBER(type, table, dim, elem, hash_function, comparator, ind, mmb);\ return(mmb);\ }\ type *sglib_hashed_##type##_it_init_on_equal(struct sglib_hashed_##type##_iterator *it, type *table[dim], int (*subcomparator)(type *, type *), type *equalto) {\ int i;\ it->table = table;\ it->subcomparator = subcomparator;\ it->equalto = equalto;\ for(i=0; i<(dim) && table[i]==NULL; i++) ;\ it->currentIndex = i;\ if (i<(dim)) return(table[i]);\ return(NULL);\ }\ type *sglib_hashed_##type##_it_init(struct sglib_hashed_##type##_iterator *it, type *table[dim]) {\ sglib_hashed_##type##_it_init_on_equal(it, table, NULL, NULL);\ }\ type *sglib_hashed_##type##_it_current(struct sglib_hashed_##type##_iterator *it) {\ return(table[it->currentIndex]);\ }\ type *sglib_hashed_##type##_it_next(struct sglib_hashed_##type##_iterator *it) {\ i=it->currentIndex;\ if (i<(dim)) {\ for(i++; i<(dim) && table[i]==NULL; i++) ;\ }\ it->currentIndex = i;\ if (i<(dim)) return(table[i]);\ return(NULL);\ } /* ------------------- hashed container (only for level 1) -------------------- */ /* hashed container is a table of given fixed size containing another (dynamic) base container in each cell. Once an object should be inserted into the hashed container, a hash function is used to determine the cell where the object belongs and the object is inserted into the base container stored in this cell. Usually the base container is simply a list or a sorted list, but it can be a red-black tree as well. parameters: type - the type of the container stored in each cell. dim - the size of the hashed array hash_function - the hashing function hashing 'type *' to unsigned. */ #define SGLIB_DEFINE_HASHED_CONTAINER_PROTOTYPES(type, dim, hash_function) \ struct sglib_hashed_##type##_iterator {\ struct sglib_##type##_iterator containerIt;\ type **table;\ int currentIndex;\ int (*subcomparator)(type *, type *);\ type *equalto;\ };\ extern void sglib_hashed_##type##_init(type *table[dim]);\ extern void sglib_hashed_##type##_add(type *table[dim], type *elem);\ extern int sglib_hashed_##type##_add_if_not_member(type *table[dim], type *elem, type **member);\ extern void sglib_hashed_##type##_delete(type *table[dim], type *elem);\ extern int sglib_hashed_##type##_delete_if_member(type *table[dim], type *elem, type **memb);\ extern int sglib_hashed_##type##_is_member(type *table[dim], type *elem);\ extern type * sglib_hashed_##type##_find_member(type *table[dim], type *elem);\ extern type *sglib_hashed_##type##_it_init(struct sglib_hashed_##type##_iterator *it, type *table[dim]); \ extern type *sglib_hashed_##type##_it_init_on_equal(struct sglib_hashed_##type##_iterator *it, type *table[dim], int (*subcomparator)(type *, type *), type *equalto); \ extern type *sglib_hashed_##type##_it_current(struct sglib_hashed_##type##_iterator *it); \ extern type *sglib_hashed_##type##_it_next(struct sglib_hashed_##type##_iterator *it); #define SGLIB_DEFINE_HASHED_CONTAINER_FUNCTIONS(type, dim, hash_function) \ /*extern unsigned hash_function(type *elem);*/\ void sglib_hashed_##type##_init(type *table[dim]) {\ unsigned i;\ for(i=0; i<(dim); i++) table[i] = NULL;\ }\ void sglib_hashed_##type##_add(type *table[dim], type *elem) {\ unsigned i;\ i = ((unsigned)hash_function(elem)) % (dim);\ sglib_##type##_add(&(table)[i], elem);\ }\ int sglib_hashed_##type##_add_if_not_member(type *table[dim], type *elem, type **member) {\ unsigned i;\ i = ((unsigned)hash_function(elem)) % (dim);\ return(sglib_##type##_add_if_not_member(&(table)[i], elem, member));\ }\ void sglib_hashed_##type##_delete(type *table[dim], type *elem) {\ unsigned i;\ i = ((unsigned)hash_function(elem)) % (dim);\ sglib_##type##_delete(&(table)[i], elem);\ }\ int sglib_hashed_##type##_delete_if_member(type *table[dim], type *elem, type **memb) {\ unsigned i;\ i = ((unsigned)hash_function(elem)) % (dim);\ return(sglib_##type##_delete_if_member(&(table)[i], elem, memb));\ }\ int sglib_hashed_##type##_is_member(type *table[dim], type *elem) {\ unsigned i;\ i = ((unsigned)hash_function(elem)) % (dim);\ return(sglib_##type##_is_member((table)[i], elem));\ }\ type * sglib_hashed_##type##_find_member(type *table[dim], type *elem) {\ unsigned i;\ i = ((unsigned)hash_function(elem)) % (dim);\ return(sglib_##type##_find_member((table)[i], elem));\ }\ type *sglib_hashed_##type##_it_init_on_equal(struct sglib_hashed_##type##_iterator *it, type *table[dim], int (*subcomparator)(type *, type *), type *equalto) {\ type *e;\ it->table = table;\ it->currentIndex = 0;\ it->subcomparator = subcomparator;\ it->equalto = equalto;\ e = sglib_##type##_it_init_on_equal(&it->containerIt, table[it->currentIndex], it->subcomparator, it->equalto);\ if (e==NULL) e = sglib_hashed_##type##_it_next(it);\ return(e);\ }\ type *sglib_hashed_##type##_it_init(struct sglib_hashed_##type##_iterator *it, type *table[dim]) {\ return(sglib_hashed_##type##_it_init_on_equal(it, table, NULL, NULL));\ }\ type *sglib_hashed_##type##_it_current(struct sglib_hashed_##type##_iterator *it) {\ return(sglib_##type##_it_current(&it->containerIt));\ }\ type *sglib_hashed_##type##_it_next(struct sglib_hashed_##type##_iterator *it) {\ type *e;\ e = sglib_##type##_it_next(&it->containerIt);\ while (e==NULL && (++(it->currentIndex))<(dim)) {\ e = sglib_##type##_it_init_on_equal(&it->containerIt, it->table[it->currentIndex], it->subcomparator, it->equalto);\ }\ return(e);\ } /* ---------------------------------------------------------------------------- */ /* ------------------------- DYNAMIC DATA STRUCTURES -------------------------- */ /* ---------------------------------------------------------------------------- */ /* ------------------------------------ list (level 1) -------------------------------- */ #define SGLIB_DEFINE_LIST_PROTOTYPES(type, comparator, next) \ struct sglib_##type##_iterator {\ type *currentelem;\ type *nextelem;\ int (*subcomparator)(type *, type *);\ type *equalto;\ };\ extern void sglib_##type##_add(type **list, type *elem);\ extern int sglib_##type##_add_if_not_member(type **list, type *elem, type **member);\ extern void sglib_##type##_concat(type **first, type *second);\ extern void sglib_##type##_delete(type **list, type *elem);\ extern int sglib_##type##_delete_if_member(type **list, type *elem, type **member);\ extern int sglib_##type##_is_member(type *list, type *elem);\ extern type *sglib_##type##_find_member(type *list, type *elem);\ extern void sglib_##type##_sort(type **list);\ extern int sglib_##type##_len(type *list);\ extern void sglib_##type##_reverse(type **list);\ extern type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list); \ extern type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto); \ extern type *sglib_##type##_it_current(struct sglib_##type##_iterator *it); \ extern type *sglib_##type##_it_next(struct sglib_##type##_iterator *it); #define SGLIB_DEFINE_LIST_FUNCTIONS(type, comparator, next) \ int sglib_##type##_is_member(type *list, type *elem) {\ int result;\ SGLIB_LIST_IS_MEMBER(type, list, elem, next, result);\ return(result);\ }\ type *sglib_##type##_find_member(type *list, type *elem) {\ type *result;\ SGLIB_LIST_FIND_MEMBER(type, list, elem, comparator, next, result);\ return(result);\ }\ int sglib_##type##_add_if_not_member(type **list, type *elem, type **member) {\ SGLIB_LIST_ADD_IF_NOT_MEMBER(type, *list, elem, comparator, next, *member);\ return(*member==NULL);\ }\ void sglib_##type##_add(type **list, type *elem) {\ SGLIB_LIST_ADD(type, *list, elem, next);\ }\ void sglib_##type##_concat(type **first, type *second) {\ SGLIB_LIST_CONCAT(type, *first, second, next);\ }\ void sglib_##type##_delete(type **list, type *elem) {\ SGLIB_LIST_DELETE(type, *list, elem, next);\ }\ int sglib_##type##_delete_if_member(type **list, type *elem, type **member) {\ SGLIB_LIST_DELETE_IF_MEMBER(type, *list, elem, comparator, next, *member);\ return(*member!=NULL);\ }\ void sglib_##type##_sort(type **list) { \ SGLIB_LIST_SORT(type, *list, comparator, next);\ }\ int sglib_##type##_len(type *list) {\ int res;\ SGLIB_LIST_LEN(type, list, next, res);\ return(res);\ }\ void sglib_##type##_reverse(type **list) {\ SGLIB_LIST_REVERSE(type, *list, next);\ }\ \ type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto) {\ it->subcomparator = subcomparator;\ it->equalto = equalto;\ it->nextelem = list;\ return(sglib_##type##_it_next(it));\ }\ type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list) {\ return(sglib_##type##_it_init_on_equal(it, list, NULL, NULL));\ }\ type *sglib_##type##_it_current(struct sglib_##type##_iterator *it) {\ return(it->currentelem);\ }\ type *sglib_##type##_it_next(struct sglib_##type##_iterator *it) {\ type *ce, *eq;\ int (*scp)(type *, type *);\ ce = it->nextelem;\ it->nextelem = NULL;\ if (it->subcomparator != NULL) {\ eq = it->equalto; \ scp = it->subcomparator;\ while (ce!=NULL && scp(ce, eq)!=0) ce = ce->next;\ }\ it->currentelem = ce;\ if (ce != NULL) it->nextelem = ce->next;\ return(ce);\ } /* ----------------------------- sorted list (level 1) ----------------------------------- */ #define SGLIB_DEFINE_SORTED_LIST_PROTOTYPES(type, comparator, next) \ struct sglib_##type##_iterator {\ type *currentelem;\ type *nextelem;\ int (*subcomparator)(type *, type *);\ type *equalto;\ };\ extern void sglib_##type##_add(type **list, type *elem);\ extern int sglib_##type##_add_if_not_member(type **list, type *elem, type **member);\ extern void sglib_##type##_delete(type **list, type *elem);\ extern int sglib_##type##_delete_if_member(type **list, type *elem, type **member);\ extern int sglib_##type##_is_member(type *list, type *elem);\ extern type *sglib_##type##_find_member(type *list, type *elem);\ extern int sglib_##type##_len(type *list);\ extern void sglib_##type##_sort(type **list);\ extern type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list); \ extern type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto); \ extern type *sglib_##type##_it_current(struct sglib_##type##_iterator *it); \ extern type *sglib_##type##_it_next(struct sglib_##type##_iterator *it); #define SGLIB_DEFINE_SORTED_LIST_FUNCTIONS(type, comparator, next) \ int sglib_##type##_is_member(type *list, type *elem) {\ int result;\ SGLIB_SORTED_LIST_IS_MEMBER(type, list, elem, comparator, next, result);\ return(result);\ }\ type *sglib_##type##_find_member(type *list, type *elem) {\ type *result;\ SGLIB_SORTED_LIST_FIND_MEMBER(type, list, elem, comparator, next, result);\ return(result);\ }\ int sglib_##type##_add_if_not_member(type **list, type *elem, type **member) {\ SGLIB_SORTED_LIST_ADD_IF_NOT_MEMBER(type, *list, elem, comparator, next, *member);\ return(*member==NULL);\ }\ void sglib_##type##_add(type **list, type *elem) {\ SGLIB_SORTED_LIST_ADD(type, *list, elem, comparator, next);\ }\ void sglib_##type##_delete(type **list, type *elem) {\ SGLIB_SORTED_LIST_DELETE(type, *list, elem, next);\ }\ int sglib_##type##_delete_if_member(type **list, type *elem, type **member) {\ SGLIB_SORTED_LIST_DELETE_IF_MEMBER(type, *list, elem, comparator, next, *member);\ return(*member!=NULL);\ }\ int sglib_##type##_len(type *list) {\ int res;\ SGLIB_SORTED_LIST_LEN(type, list, next, res);\ return(res);\ }\ void sglib_##type##_sort(type **list) { \ SGLIB_LIST_SORT(type, *list, comparator, next);\ }\ \ type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto) {\ it->subcomparator = subcomparator;\ it->equalto = equalto;\ it->nextelem = list;\ return(sglib_##type##_it_next(it));\ }\ type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list) {\ return(sglib_##type##_it_init_on_equal(it, list, NULL, NULL));\ }\ type *sglib_##type##_it_current(struct sglib_##type##_iterator *it) {\ return(it->currentelem);\ }\ type *sglib_##type##_it_next(struct sglib_##type##_iterator *it) {\ type *ce, *eq;\ int (*scp)(type *, type *);\ int c;\ ce = it->nextelem;\ it->nextelem = NULL;\ if (it->subcomparator != NULL) {\ eq = it->equalto; \ scp = it->subcomparator;\ while (ce!=NULL && (c=scp(ce, eq)) < 0) ce = ce->next;\ if (ce != NULL && c > 0) ce = NULL;\ }\ it->currentelem = ce;\ if (ce != NULL) it->nextelem = ce->next;\ return(ce);\ } /* ----------------------------- double linked list (level 1) ------------------------------ */ #define SGLIB_DEFINE_DL_LIST_PROTOTYPES(type, comparator, previous, next) \ struct sglib_##type##_iterator {\ type *currentelem;\ type *prevelem;\ type *nextelem;\ int (*subcomparator)(type *, type *);\ type *equalto;\ };\ extern void sglib_##type##_add(type **list, type *elem);\ extern void sglib_##type##_add_before(type **list, type *elem);\ extern void sglib_##type##_add_after(type **list, type *elem);\ extern int sglib_##type##_add_if_not_member(type **list, type *elem, type **member);\ extern int sglib_##type##_add_before_if_not_member(type **list, type *elem, type **member);\ extern int sglib_##type##_add_after_if_not_member(type **list, type *elem, type **member);\ extern void sglib_##type##_concat(type **first, type *second);\ extern void sglib_##type##_delete(type **list, type *elem);\ extern int sglib_##type##_delete_if_member(type **list, type *elem, type **member);\ extern int sglib_##type##_is_member(type *list, type *elem);\ extern type *sglib_##type##_find_member(type *list, type *elem);\ extern type *sglib_##type##_get_first(type *list);\ extern type *sglib_##type##_get_last(type *list);\ extern void sglib_##type##_sort(type **list);\ extern int sglib_##type##_len(type *list);\ extern void sglib_##type##_reverse(type **list);\ extern type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list); \ extern type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto); \ extern type *sglib_##type##_it_current(struct sglib_##type##_iterator *it); \ extern type *sglib_##type##_it_next(struct sglib_##type##_iterator *it); #define SGLIB_DEFINE_DL_LIST_FUNCTIONS(type, comparator, previous, next) \ void sglib_##type##_add(type **list, type *elem) {\ SGLIB_DL_LIST_ADD(type, *list, elem, previous, next);\ }\ void sglib_##type##_add_after(type **list, type *elem) {\ SGLIB_DL_LIST_ADD_AFTER(type, *list, elem, previous, next);\ }\ void sglib_##type##_add_before(type **list, type *elem) {\ SGLIB_DL_LIST_ADD_BEFORE(type, *list, elem, previous, next);\ }\ int sglib_##type##_add_if_not_member(type **list, type *elem, type **member) {\ SGLIB_DL_LIST_ADD_IF_NOT_MEMBER(type, *list, elem, comparator, previous, next, *member);\ return(*member==NULL);\ }\ int sglib_##type##_add_after_if_not_member(type **list, type *elem, type **member) {\ SGLIB_DL_LIST_ADD_AFTER_IF_NOT_MEMBER(type, *list, elem, comparator, previous, next, *member);\ return(*member==NULL);\ }\ int sglib_##type##_add_before_if_not_member(type **list, type *elem, type **member) {\ SGLIB_DL_LIST_ADD_BEFORE_IF_NOT_MEMBER(type, *list, elem, comparator, previous, next, *member);\ return(*member==NULL);\ }\ void sglib_##type##_concat(type **first, type *second) {\ SGLIB_DL_LIST_CONCAT(type, *first, second, previous, next);\ }\ void sglib_##type##_delete(type **list, type *elem) {\ SGLIB_DL_LIST_DELETE(type, *list, elem, previous, next);\ }\ int sglib_##type##_delete_if_member(type **list, type *elem, type **member) {\ SGLIB_DL_LIST_DELETE_IF_MEMBER(type, *list, elem, comparator, previous, next, *member);\ return(*member!=NULL);\ }\ int sglib_##type##_is_member(type *list, type *elem) {\ int result;\ SGLIB_DL_LIST_IS_MEMBER(type, list, elem, previous, next, result);\ return(result);\ }\ type *sglib_##type##_find_member(type *list, type *elem) {\ type *result;\ SGLIB_DL_LIST_FIND_MEMBER(type, list, elem, comparator, previous, next, result);\ return(result);\ }\ type *sglib_##type##_get_first(type *list) {\ type *result;\ SGLIB_DL_LIST_GET_FIRST(type, list, previous, next, result);\ return(result);\ }\ type *sglib_##type##_get_last(type *list) {\ type *result;\ SGLIB_DL_LIST_GET_LAST(type, list, previous, next, result);\ return(result);\ }\ void sglib_##type##_sort(type **list) {\ SGLIB_DL_LIST_SORT(type, *list, comparator, previous, next);\ }\ int sglib_##type##_len(type *list) {\ int res;\ SGLIB_DL_LIST_LEN(type, list, previous, next, res);\ return(res);\ }\ void sglib_##type##_reverse(type **list) {\ SGLIB_DL_LIST_REVERSE(type, *list, previous, next);\ }\ \ type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *list, int (*subcomparator)(type *, type *), type *equalto) {\ it->subcomparator = subcomparator;\ it->equalto = equalto;\ it->prevelem = list;\ it->nextelem = list;\ if (list != NULL) it->nextelem = list->next;\ return(sglib_##type##_it_next(it));\ }\ type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *list) {\ return(sglib_##type##_it_init_on_equal(it, list, NULL, NULL));\ }\ type *sglib_##type##_it_current(struct sglib_##type##_iterator *it) {\ return(it->currentelem);\ }\ type *sglib_##type##_it_next(struct sglib_##type##_iterator *it) {\ type *ce, *eq;\ int (*scp)(type *, type *);\ ce = it->prevelem;\ it->prevelem = NULL;\ if (it->subcomparator != NULL) {\ eq = it->equalto; \ scp = it->subcomparator;\ while (ce!=NULL && scp(eq, ce)!=0) ce = ce->previous;\ }\ if (ce != NULL) {\ it->prevelem = ce->previous;\ } else {\ ce = it->nextelem;\ it->nextelem = NULL;\ if (it->subcomparator != NULL) {\ eq = it->equalto; \ scp = it->subcomparator;\ while (ce!=NULL && scp(ce, eq)!=0) ce = ce->next;\ }\ if (ce != NULL) it->nextelem = ce->next;\ }\ it->currentelem = ce;\ return(ce);\ } /* --------------------------------- red-black trees (level 1) -------------------------------- */ /* This implementation requires pointers to left and right sons (no parent pointer is needed) and one bit of additional information storing the color of the node. The implementation follows discrepancy fixing rules from: http://www.cis.ohio-state.edu/~gurari/course/cis680/cis680Ch11.html */ #define SGLIB___RBTREE_FIX_INSERTION_DISCREPANCY(type, tree, leftt, rightt, bits, RED, BLACK) {\ type *t, *tl, *a, *b, *c, *ar, *bl, *br, *cl, *cr;\ t = *tree;\ tl = t->leftt;\ if (t->rightt!=NULL && SGLIB___GET_VALUE(t->rightt->bits)==RED) {\ if (SGLIB___GET_VALUE(tl->bits)==RED) {\ if ((tl->leftt!=NULL && SGLIB___GET_VALUE(tl->leftt->bits)==RED) \ || (tl->rightt!=NULL && SGLIB___GET_VALUE(tl->rightt->bits)==RED)) {\ SGLIB___SET_VALUE(t->leftt->bits,BLACK);\ SGLIB___SET_VALUE(t->rightt->bits,BLACK);\ SGLIB___SET_VALUE(t->bits,RED);\ }\ }\ } else {\ if (SGLIB___GET_VALUE(tl->bits)==RED) {\ if (tl->leftt!=NULL && SGLIB___GET_VALUE(tl->leftt->bits)==RED) {\ a = t; b = tl; c = tl->leftt;\ br = b->rightt;\ a->leftt = br;\ b->leftt = c; b->rightt = a;\ SGLIB___SET_VALUE(a->bits,RED);\ SGLIB___SET_VALUE(b->bits,BLACK);\ *tree = b;\ } else if (tl->rightt!=NULL && SGLIB___GET_VALUE(tl->rightt->bits)==RED) {\ a = t; b = tl; ar=a->rightt;\ bl=b->leftt; c=b->rightt;\ cl=c->leftt; cr=c->rightt;\ b->rightt = cl;\ a->leftt = cr;\ c->leftt = b;\ c->rightt = a;\ SGLIB___SET_VALUE(c->bits,BLACK);\ SGLIB___SET_VALUE(a->bits,RED);\ *tree = c;\ }\ }\ }\ } #define SGLIB___RBTREE_FIX_DELETION_DISCREPANCY(type, tree, leftt, rightt, bits, RED, BLACK, res) {\ type *t, *a, *b, *c, *d, *ar, *bl, *br, *cl, *cr, *dl, *dr;\ t = a = *tree;\ assert(t!=NULL);\ ar = a->rightt;\ b = t->leftt;\ if (b==NULL) {\ assert(SGLIB___GET_VALUE(t->bits)==RED);\ SGLIB___SET_VALUE(t->bits,BLACK);\ res = 0;\ } else {\ bl = b->leftt;\ br = b->rightt;\ if (SGLIB___GET_VALUE(b->bits)==RED) {\ if (br==NULL) {\ *tree = b;\ SGLIB___SET_VALUE(b->bits,BLACK);\ b->rightt = a;\ a->leftt = br;\ res = 0;\ } else {\ c = br;\ assert(c!=NULL && SGLIB___GET_VALUE(c->bits)==BLACK);\ cl = c->leftt;\ cr = c->rightt;\ if ((cl==NULL||SGLIB___GET_VALUE(cl->bits)==BLACK) && (cr==NULL||SGLIB___GET_VALUE(cr->bits)==BLACK)) {\ *tree = b;\ b->rightt = a;\ SGLIB___SET_VALUE(b->bits,BLACK);\ a->leftt = c;\ SGLIB___SET_VALUE(c->bits,RED);\ res = 0;\ } else if (cl!=NULL && SGLIB___GET_VALUE(cl->bits)==RED) {\ if (cr!=NULL && SGLIB___GET_VALUE(cr->bits)==RED) {\ d = cr;\ dl = d->leftt;\ dr = d->rightt;\ *tree = d;\ SGLIB___SET_VALUE(d->bits,BLACK);\ d->leftt = b;\ c->rightt = dl;\ d->rightt = a;\ a->leftt = dr;\ res = 0;\ } else {\ *tree = c;\ c->leftt = b;\ c->rightt = a;\ b->leftt = bl;\ b->rightt = cl;\ a->leftt = cr;\ SGLIB___SET_VALUE(cl->bits,BLACK);\ res = 0;\ }\ } else if (cr!=NULL && SGLIB___GET_VALUE(cr->bits)==RED) {\ assert(cl==NULL || SGLIB___GET_VALUE(cl->bits)==BLACK);\ d = cr;\ dl = d->leftt;\ dr = d->rightt;\ *tree = d;\ SGLIB___SET_VALUE(d->bits,BLACK);\ d->leftt = b;\ c->rightt = dl;\ d->rightt = a;\ a->leftt = dr;\ res = 0;\ } else {\ assert(0);\ res = 0;\ }\ }\ } else {\ if ((bl==NULL || SGLIB___GET_VALUE(bl->bits)==BLACK) && (br==NULL || SGLIB___GET_VALUE(br->bits)==BLACK)) {\ res = (SGLIB___GET_VALUE(a->bits)==BLACK);\ SGLIB___SET_VALUE(a->bits,BLACK);\ SGLIB___SET_VALUE(b->bits,RED);\ } else if (bl!=NULL && SGLIB___GET_VALUE(bl->bits)==RED) {\ if (br==NULL || SGLIB___GET_VALUE(br->bits)==BLACK) {\ *tree = b;\ SGLIB___SET_VALUE(b->bits,SGLIB___GET_VALUE(a->bits));\ SGLIB___SET_VALUE(a->bits,BLACK);\ b->rightt = a;\ a->leftt = br;\ SGLIB___SET_VALUE(bl->bits,BLACK);\ res = 0;\ } else {\ assert(bl!=NULL);\ assert(br!=NULL);\ assert(SGLIB___GET_VALUE(bl->bits)==RED);\ assert(SGLIB___GET_VALUE(br->bits)==RED);\ c = br;\ cl = c->leftt;\ cr = c->rightt;\ *tree = c;\ SGLIB___SET_VALUE(c->bits,SGLIB___GET_VALUE(a->bits));\ SGLIB___SET_VALUE(a->bits,BLACK);\ c->leftt = b;\ c->rightt = a;\ b->rightt = cl;\ a->leftt = cr;\ res = 0;\ }\ } else {\ assert(br!=NULL && SGLIB___GET_VALUE(br->bits)==RED);\ c = br;\ cl = c->leftt;\ cr = c->rightt;\ *tree = c;\ SGLIB___SET_VALUE(c->bits,SGLIB___GET_VALUE(a->bits));\ SGLIB___SET_VALUE(a->bits,BLACK);\ c->leftt = b;\ c->rightt = a;\ b->rightt = cl;\ a->leftt = cr;\ res = 0;\ }\ }\ }\ } #define SGLIB_DEFINE_RBTREE_FUNCTIONS_GENERAL(type, left, right, bits, comparator, RED, BLACK) \ static void sglib___##type##_fix_left_insertion_discrepancy(type **tree) {\ SGLIB___RBTREE_FIX_INSERTION_DISCREPANCY(type, tree, left, right, bits, RED, BLACK);\ }\ \ static void sglib___##type##_fix_right_insertion_discrepancy(type **tree) {\ SGLIB___RBTREE_FIX_INSERTION_DISCREPANCY(type, tree, right, left, bits, RED, BLACK);\ }\ \ static int sglib___##type##_fix_left_deletion_discrepancy(type **tree) {\ int res;\ SGLIB___RBTREE_FIX_DELETION_DISCREPANCY(type, tree, right, left, bits, RED, BLACK, res);\ return(res);\ }\ \ static int sglib___##type##_fix_right_deletion_discrepancy(type **tree) {\ int res;\ SGLIB___RBTREE_FIX_DELETION_DISCREPANCY(type, tree, left, right, bits, RED, BLACK, res);\ return(res);\ }\ \ static void sglib___##type##_add_recursive(type **tree, type *elem) {\ int cmp;\ type *t;\ t = *tree;\ if (t == NULL) {\ SGLIB___SET_VALUE(elem->bits,RED);\ *tree =elem;\ } else {\ cmp = comparator(elem, t);\ if (cmp < 0 || (cmp==0 && elemleft, elem);\ if (SGLIB___GET_VALUE(t->bits)==BLACK) sglib___##type##_fix_left_insertion_discrepancy(tree);\ } else {\ sglib___##type##_add_recursive(&t->right, elem);\ if (SGLIB___GET_VALUE(t->bits)==BLACK) sglib___##type##_fix_right_insertion_discrepancy(tree);\ }\ }\ }\ \ static int sglib___##type##_delete_rightmost_leaf(type **tree, type **theLeaf) {\ type *t;\ int res, deepDecreased;\ t = *tree;\ res = 0;\ assert(t!=NULL);\ if (t->right == NULL) {\ *theLeaf = t;\ if (t->left!=NULL) {\ if (SGLIB___GET_VALUE(t->bits)==BLACK && SGLIB___GET_VALUE(t->left->bits)==BLACK) res = 1;\ SGLIB___SET_VALUE(t->left->bits,BLACK);\ *tree = t->left;\ } else {\ *tree = NULL;\ res = (SGLIB___GET_VALUE(t->bits)==BLACK);\ }\ } else {\ deepDecreased = sglib___##type##_delete_rightmost_leaf(&t->right, theLeaf);\ if (deepDecreased) res = sglib___##type##_fix_right_deletion_discrepancy(tree);\ }\ return(res);\ }\ \ int sglib___##type##_delete_recursive(type **tree, type *elem) {\ type *t, *theLeaf;\ int cmp, res, deepDecreased;\ t = *tree;\ res = 0;\ if (t==NULL) {\ assert(0 && "The element to delete not found in the tree, use 'delete_if_member'"!=NULL);\ } else {\ cmp = comparator(elem, t);\ if (cmp < 0 || (cmp==0 && elemleft, elem);\ if (deepDecreased) {\ res = sglib___##type##_fix_left_deletion_discrepancy(tree);\ }\ } else if (cmp > 0 || (cmp==0 && elem>t)) {\ deepDecreased = sglib___##type##_delete_recursive(&t->right, elem);\ if (deepDecreased) {\ res = sglib___##type##_fix_right_deletion_discrepancy(tree);\ }\ } else {\ assert(elem==t && "Deleting an element which is non member of the tree, use 'delete_if_member'"!=NULL);\ if (t->left == NULL) {\ if (t->right == NULL) {\ /* a leaf, delete, it; */\ *tree = NULL;\ res = (SGLIB___GET_VALUE(t->bits)==BLACK);\ } else {\ if (SGLIB___GET_VALUE(t->bits)==0 && SGLIB___GET_VALUE(t->right->bits)==0) res = 1;\ SGLIB___SET_VALUE(t->right->bits,BLACK);\ *tree = t->right;\ }\ } else {\ /* propagate deletion until righmost leaf of left subtree */\ deepDecreased = sglib___##type##_delete_rightmost_leaf(&t->left, &theLeaf);\ theLeaf->left = t->left;\ theLeaf->right = t->right;\ SGLIB___SET_VALUE(theLeaf->bits,SGLIB___GET_VALUE(t->bits));\ *tree = theLeaf;\ if (deepDecreased) res = sglib___##type##_fix_left_deletion_discrepancy(tree);\ }\ }\ }\ return(res);\ }\ \ void sglib_##type##_add(type **tree, type *elem) {\ elem->left = elem->right = NULL;\ sglib___##type##_add_recursive(tree, elem);\ SGLIB___SET_VALUE((*tree)->bits,BLACK);\ }\ \ void sglib_##type##_delete(type **tree, type *elem) {\ sglib___##type##_delete_recursive(tree, elem);\ if (*tree!=NULL) SGLIB___SET_VALUE((*tree)->bits,BLACK);\ }\ \ type *sglib_##type##_find_member(type *t, type *elem) {\ type *res;\ SGLIB___BIN_TREE_FIND_MEMBER(type, t, elem, left, right, comparator, res);\ return(res);\ }\ \ int sglib_##type##_is_member(type *t, type *elem) {\ int cmp;\ while (t!=NULL) {\ cmp = comparator(elem, t);\ if (cmp < 0 || (cmp==0 && elemleft;\ } else if (cmp > 0 || (cmp==0 && elem>t)) {\ t = t->right;\ } else {\ assert(t == elem);\ return(1);\ }\ }\ return(0);\ }\ \ int sglib_##type##_delete_if_member(type **tree, type *elem, type **memb) {\ if ((*memb=sglib_##type##_find_member(*tree, elem))!=NULL) {\ sglib_##type##_delete(tree, *memb);\ return(1);\ } else {\ return(0);\ }\ }\ int sglib_##type##_add_if_not_member(type **tree, type *elem, type **memb) {\ if ((*memb=sglib_##type##_find_member(*tree, elem))==NULL) {\ sglib_##type##_add(tree, elem);\ return(1);\ } else {\ return(0);\ }\ }\ int sglib_##type##_len(type *t) {\ int n;\ type *e;\ n = 0;\ SGLIB_BIN_TREE_MAP_ON_ELEMENTS(type, t, e, left, right, n++);\ return(n);\ }\ \ void sglib__##type##_it_compute_current_elem(struct sglib_##type##_iterator *it) {\ int i,j,cmp;\ type *s, *eqt;\ int (*subcomparator)(type *, type *);\ eqt = it->equalto;\ subcomparator = it->subcomparator;\ it->currentelem = NULL;\ while(it->pathi > 0 && it->currentelem==NULL) {\ i = it->pathi-1;\ if (i >= 0) {\ if (it->pass[i] >= 2) {\ /* goto up */\ it->pathi --;\ } else {\ if (it->pass[i] == 0) {\ /* goto left */\ s = it->path[i]->left;\ } else {\ /* goto right */\ s = it->path[i]->right;\ }\ if (eqt != NULL) {\ if (subcomparator == NULL) {\ SGLIB___BIN_TREE_FIND_MEMBER(type, s, eqt, left, right, comparator, s);\ } else {\ SGLIB___BIN_TREE_FIND_MEMBER(type, s, eqt, left, right, subcomparator, s);\ }\ }\ if (s != NULL) {\ j = i+1;\ it->path[j] = s;\ it->pass[j] = 0;\ it->pathi ++;\ }\ it->pass[i] ++;\ }\ }\ if (it->pathi>0 && it->order == it->pass[it->pathi-1]) {\ it->currentelem = it->path[it->pathi-1];\ }\ }\ }\ type *sglib__##type##_it_init(struct sglib_##type##_iterator *it, type *tree, int order, int (*subcomparator)(type *, type *), type *equalto) {\ type *t;\ assert(it!=NULL);\ it->order = order;\ it->equalto = equalto;\ it->subcomparator = subcomparator;\ if (equalto == NULL) { \ t = tree;\ } else {\ if (subcomparator == NULL) {\ SGLIB___BIN_TREE_FIND_MEMBER(type, tree, equalto, left, right, comparator, t);\ } else {\ SGLIB___BIN_TREE_FIND_MEMBER(type, tree, equalto, left, right, subcomparator, t);\ }\ }\ if (t == NULL) {\ it->pathi = 0;\ it->currentelem = NULL;\ } else {\ it->pathi = 1;\ it->pass[0] = 0;\ it->path[0] = t;\ if (order == 0) {\ it->currentelem = t;\ } else {\ sglib__##type##_it_compute_current_elem(it);\ }\ }\ return(it->currentelem);\ }\ type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *tree) {\ return(sglib__##type##_it_init(it, tree, 2, NULL, NULL));\ }\ type *sglib_##type##_it_init_preorder(struct sglib_##type##_iterator *it, type *tree) {\ return(sglib__##type##_it_init(it, tree, 0, NULL, NULL));\ }\ type *sglib_##type##_it_init_inorder(struct sglib_##type##_iterator *it, type *tree) {\ return(sglib__##type##_it_init(it, tree, 1, NULL, NULL));\ }\ type *sglib_##type##_it_init_postorder(struct sglib_##type##_iterator *it, type *tree) {\ return(sglib__##type##_it_init(it, tree, 2, NULL, NULL));\ }\ type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *tree, int (*subcomparator)(type *, type *), type *equalto) {\ return(sglib__##type##_it_init(it, tree, 1, subcomparator, equalto));\ }\ type *sglib_##type##_it_current(struct sglib_##type##_iterator *it) {\ return(it->currentelem);\ }\ type *sglib_##type##_it_next(struct sglib_##type##_iterator *it) {\ sglib__##type##_it_compute_current_elem(it);\ return(it->currentelem);\ }\ \ static void sglib___##type##_consistency_check_recursive(type *t, int *pathdeep, int cdeep) {\ if (t==NULL) {\ if (*pathdeep < 0) *pathdeep = cdeep;\ else assert(*pathdeep == cdeep);\ } else {\ if (t->left!=NULL) assert(comparator(t->left, t) <= 0);\ if (t->right!=NULL) assert(comparator(t, t->right) <= 0);\ if (SGLIB___GET_VALUE(t->bits) == RED) {\ assert(t->left == NULL || SGLIB___GET_VALUE(t->left->bits)==BLACK);\ assert(t->right == NULL || SGLIB___GET_VALUE(t->right->bits)==BLACK);\ sglib___##type##_consistency_check_recursive(t->left, pathdeep, cdeep);\ sglib___##type##_consistency_check_recursive(t->right, pathdeep, cdeep);\ } else {\ sglib___##type##_consistency_check_recursive(t->left, pathdeep, cdeep+1);\ sglib___##type##_consistency_check_recursive(t->right, pathdeep, cdeep+1);\ }\ }\ }\ \ void sglib___##type##_consistency_check(type *t) {\ int pathDeep;\ assert(t==NULL || SGLIB___GET_VALUE(t->bits) == BLACK);\ pathDeep = -1;\ sglib___##type##_consistency_check_recursive(t, &pathDeep, 0);\ } #define SGLIB_DEFINE_RBTREE_PROTOTYPES(type, left, right, colorbit, comparator) \ struct sglib_##type##_iterator {\ type *currentelem;\ char pass[SGLIB_MAX_TREE_DEEP];\ type *path[SGLIB_MAX_TREE_DEEP];\ short int pathi;\ short int order;\ type *equalto;\ int (*subcomparator)(type *, type *);\ };\ extern void sglib___##type##_consistency_check(type *t); \ extern void sglib_##type##_add(type **tree, type *elem); \ extern int sglib_##type##_add_if_not_member(type **tree, type *elem, type **memb); \ extern void sglib_##type##_delete(type **tree, type *elem); \ extern int sglib_##type##_delete_if_member(type **tree, type *elem, type **memb); \ extern int sglib_##type##_is_member(type *t, type *elem); \ extern type *sglib_##type##_find_member(type *t, type *elem); \ extern int sglib_##type##_len(type *t); \ extern type *sglib_##type##_it_init(struct sglib_##type##_iterator *it, type *tree); \ extern type *sglib_##type##_it_init_preorder(struct sglib_##type##_iterator *it, type *tree); \ extern type *sglib_##type##_it_init_inorder(struct sglib_##type##_iterator *it, type *tree); \ extern type *sglib_##type##_it_init_postorder(struct sglib_##type##_iterator *it, type *tree); \ extern type *sglib_##type##_it_init_on_equal(struct sglib_##type##_iterator *it, type *tree, int (*subcomparator)(type *, type *), type *equalto); \ extern type *sglib_##type##_it_current(struct sglib_##type##_iterator *it); \ extern type *sglib_##type##_it_next(struct sglib_##type##_iterator *it); \ #define SGLIB_DEFINE_RBTREE_FUNCTIONS(type, left, right, colorbit, comparator) \ SGLIB_DEFINE_RBTREE_FUNCTIONS_GENERAL(type, left, right, colorbit, comparator, 1, 0) /* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */ /* - SUPPLEMENTARY DEFINITIONS - */ /* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */ #define SGLIB___GET_VALUE(x) (x) #define SGLIB___SET_VALUE(x, value) {(x) = (value);} #define SGLIB_ARRAY_ELEMENTS_EXCHANGER(type, a, i, j) {type _sgl_aee_tmp_; _sgl_aee_tmp_=(a)[(i)]; (a)[(i)]=(a)[(j)]; (a)[(j)]= _sgl_aee_tmp_;} #define SGLIB_SAFE_NUMERIC_COMPARATOR(x, y) (((x)>(y)?1:((x)<(y)?-1:0))) #define SGLIB_SAFE_REVERSE_NUMERIC_COMPARATOR(x, y) (((x)>(y)?-1:((x)<(y)?1:0))) #define SGLIB_FAST_NUMERIC_COMPARATOR(x, y) ((int)((x) - (y))) #define SGLIB_FAST_REVERSE_NUMERIC_COMPARATOR(x, y) ((int)((y) - (x))) #define SGLIB_NUMERIC_COMPARATOR(x, y) SGLIB_SAFE_NUMERIC_COMPARATOR(x, y) #define SGLIB_REVERSE_NUMERIC_COMPARATOR(x, y) SGLIB_SAFE_REVERSE_NUMERIC_COMPARATOR(x, y) #ifndef SGLIB_MAX_TREE_DEEP #define SGLIB_MAX_TREE_DEEP 128 #endif #ifndef SGLIB_HASH_TAB_SHIFT_CONSTANT #define SGLIB_HASH_TAB_SHIFT_CONSTANT 16381 /* should be a prime */ /* #define SGLIB_HASH_TAB_SHIFT_CONSTANT 536870912*/ /* for large tables :) */ #endif #endif /* _SGLIB__h_ */ ruby-patron-0.4.18/lib/000077500000000000000000000000001204444370700146415ustar00rootroot00000000000000ruby-patron-0.4.18/lib/patron.rb000066400000000000000000000031161204444370700164720ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Patron HTTP Client: Bootstrap script ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require 'pathname' cwd = Pathname(__FILE__).dirname $:.unshift(cwd.to_s) unless $:.include?(cwd.to_s) || $:.include?(cwd.expand_path.to_s) require 'patron/session' module Patron #:nodoc: # Returns the version number of the Patron library as a string def self.version VERSION end end ruby-patron-0.4.18/lib/patron/000077500000000000000000000000001204444370700161445ustar00rootroot00000000000000ruby-patron-0.4.18/lib/patron/error.rb000066400000000000000000000044421204444370700176260ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Patron HTTP Client: Error definitions ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- module Patron # Base class for Patron exceptions. class Error < StandardError; end # The URL you passed to Patron used a protocol that it does not support. # This most likely the result of a misspelled protocol string. class UnsupportedProtocol < Error; end # The URL was not properly formatted. class URLFormatError < Error; end # Could not resolve the remote host name. class HostResolutionError < Error; end # Failed to connect to the remote host. class ConnectionFailed < Error; end # A file transfer was shorter or larger than expected. # This happens when the server first reports an expected transfer size, # and then delivers data that doesn't match the previously given size. class PartialFileError < Error; end # Operation timeout. The specified time-out period was reached. class TimeoutError < Error; end # Too many redirects. When following redirects, Patron hit the maximum amount. class TooManyRedirects < Error; end end ruby-patron-0.4.18/lib/patron/proxy_type.rb000066400000000000000000000001671204444370700207170ustar00rootroot00000000000000module Patron module ProxyType HTTP HTTP_1_0 SOCKS4 SOCKS5 SOCKS4A SOCKS5_HOSTNAME end end ruby-patron-0.4.18/lib/patron/request.rb000066400000000000000000000104131204444370700201600ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Patron HTTP Client: Request class ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require 'patron/util' module Patron # Represents the information necessary for an HTTP request. # This is basically a data object with validation. Not all fields will be # used in every request. class Request VALID_ACTIONS = [:get, :put, :post, :delete, :head, :copy] def initialize @action = :get @headers = {} @timeout = 0 @connect_timeout = 0 @max_redirects = -1 end attr_accessor :url, :username, :password, :file_name, :proxy, :proxy_type, :auth_type, :insecure, :ignore_content_length, :multipart attr_reader :action, :timeout, :connect_timeout, :max_redirects, :headers, :buffer_size attr_reader :auth_type # Set the type of authentication to use for this request. # # @param [String, Symbol] type - The type of authentication to use for this request, can be one of # :basic, :digest, or :any # # @example # sess.username = "foo" # sess.password = "sekrit" # sess.auth_type = :digest def auth_type=(type=:basic) @auth_type = case type when :basic, "basic" Request::AuthBasic when :digest, "digest" Request::AuthDigest when :any, "any" Request::AuthAny else raise "#{type.inspect} is an unknown authentication type" end end def upload_data=(data) @upload_data = case data when Hash self.multipart ? data : Util.build_query_string_from_hash(data, @action == :post) else data end end def upload_data @upload_data end def action=(new_action) if !VALID_ACTIONS.include?(new_action) raise ArgumentError, "Action must be one of #{VALID_ACTIONS.join(', ')}" end @action = new_action end def timeout=(new_timeout) if new_timeout && new_timeout.to_i < 1 raise ArgumentError, "Timeout must be a positive integer greater than 0" end @timeout = new_timeout.to_i end def connect_timeout=(new_timeout) if new_timeout && new_timeout.to_i < 1 raise ArgumentError, "Timeout must be a positive integer greater than 0" end @connect_timeout = new_timeout.to_i end def max_redirects=(new_max_redirects) if new_max_redirects.to_i < -1 raise ArgumentError, "Max redirects must be a positive integer, 0 or -1" end @max_redirects = new_max_redirects.to_i end def headers=(new_headers) if !new_headers.kind_of?(Hash) raise ArgumentError, "Headers must be a hash" end @headers = new_headers end def buffer_size=(buffer_size) if buffer_size != nil && buffer_size.to_i < 1 raise ArgumentError, "Buffer size must be a positive integer greater than 0 or nil" end @buffer_size = buffer_size != nil ? buffer_size.to_i : nil end def action_name @action.to_s.upcase end def credentials return nil if username.nil? || password.nil? "#{username}:#{password}" end end end ruby-patron-0.4.18/lib/patron/response.rb000066400000000000000000000066541204444370700203420ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Patron HTTP Client: Response class ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- module Patron # Represents the response from the HTTP server. class Response def initialize(url, status, redirect_count, header_data, body, default_charset = nil) # Don't let a response clear out the default charset, which would cause encoding to fail default_charset = "ASCII-8BIT" unless default_charset @url = url @status = status @redirect_count = redirect_count @body = body @charset = determine_charset(header_data, body) || default_charset [url, header_data].each do |attr| convert_to_default_encoding!(attr) end parse_headers(header_data) if @headers["Content-Type"] && @headers["Content-Type"][0, 5] == "text/" convert_to_default_encoding!(@body) end end attr_reader :url, :status, :status_line, :redirect_count, :body, :headers, :charset def inspect # Avoid spamming the console with the header and body data "#" end private def determine_charset(header_data, body) header_data.match(charset_regex) || (body && body.match(charset_regex)) $1 end def charset_regex /(?:charset|encoding)="?([a-z0-9-]+)"?/i end def convert_to_default_encoding!(str) if str.respond_to?(:encode) && Encoding.default_internal str.force_encoding(charset).encode!(Encoding.default_internal) end end # Called by the C code to parse and set the headers def parse_headers(header_data) @headers = {} header_data.split(/\r\n/).each do |header| if header =~ %r|^HTTP/1.[01]| @status_line = header.strip else parts = header.split(':', 2) unless parts.empty? parts[1].strip! unless parts[1].nil? if @headers.has_key?(parts[0]) @headers[parts[0]] = [@headers[parts[0]]] unless @headers[parts[0]].kind_of? Array @headers[parts[0]] << parts[1] else @headers[parts[0]] = parts[1] end end end end end end end ruby-patron-0.4.18/lib/patron/session.rb000066400000000000000000000210211204444370700201500ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Patron HTTP Client: Session class ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require 'uri' require 'patron/error' require 'patron/request' require 'patron/response' require 'patron/session_ext' require 'patron/util' module Patron # This class represents multiple request/response transactions with an HTTP # server. This is the primary API for Patron. class Session # HTTP connection timeout in seconds. Defaults to 1 second. attr_accessor :connect_timeout # HTTP transaction timeout in seconds. Defaults to 5 seconds. attr_accessor :timeout # Maximum number of times to follow redirects. # Set to 0 to disable and -1 to follow all redirects. Defaults to 5. attr_accessor :max_redirects # Prepended to the URL in all requests. attr_accessor :base_url # Username and password for http authentication attr_accessor :username, :password # Proxy URL in cURL format ('hostname:8080') attr_accessor :proxy # Proxy type (default is HTTP), see constants under ProxyType for supported types. attr_accessor :proxy_type # Standard set of headers that are used in all requests. attr_reader :headers # Set the authentication type for the request. # @see Patron::Request#auth_type attr_accessor :auth_type # Does this session stricly verify SSL certificates? attr_accessor :insecure # Does this session ignore Content-Size headers? attr_accessor :ignore_content_length # Set the buffer size for this request. This option will # only be set if buffer_size is non-nil attr_accessor :buffer_size # Default encoding of responses. Used if no charset is provided by the host. attr_accessor :default_response_charset private :handle_request, :enable_cookie_session, :set_debug_file # Create a new Session object. def initialize @headers = {} @timeout = 5 @connect_timeout = 1 @max_redirects = 5 @auth_type = :basic end # Turn on cookie handling for this session, storing them in memory by # default or in +file+ if specified. The +file+ must be readable and # writable. Calling multiple times will add more files. def handle_cookies(file = nil) if file path = Pathname(file).expand_path unless File.exists?(file) and File.writable?(path.dirname) raise ArgumentError, "Can't create file #{path} (permission error)" end unless File.readable?(file) or File.writable?(path) raise ArgumentError, "Can't read or write file #{path} (permission error)" end end enable_cookie_session(path.to_s) self end # Enable debug output to stderr or to specified +file+. def enable_debug(file = nil) set_debug_file(file.to_s) end ################################################################### ### Standard HTTP methods ### # Retrieve the contents of the specified +url+ optionally sending the # specified headers. If the +base_url+ varaible is set then it is prepended # to the +url+ parameter. Any custom headers are merged with the contents # of the +headers+ instance variable. The results are returned in a # Response object. # Notice: this method doesn't accept any +data+ argument: if you need to send data with # a get request, please, use the #request method. def get(url, headers = {}) request(:get, url, headers) end # Retrieve the contents of the specified +url+ as with #get, but the # content at the URL is downloaded directly into the specified file. def get_file(url, filename, headers = {}) request(:get, url, headers, :file => filename) end # As #get but sends an HTTP HEAD request. def head(url, headers = {}) request(:head, url, headers) end # As #get but sends an HTTP DELETE request. def delete(url, headers = {}) request(:delete, url, headers) end # Uploads the passed +data+ to the specified +url+ using HTTP PUT. +data+ # must be a string. def put(url, data, headers = {}) request(:put, url, headers, :data => data) end # Uploads the contents of a file to the specified +url+ using HTTP PUT. def put_file(url, filename, headers = {}) request(:put, url, headers, :file => filename) end # Uploads the passed +data+ to the specified +url+ using HTTP POST. +data+ # can be a string or a hash. def post(url, data, headers = {}) if data.is_a?(Hash) data = data.map {|k,v| urlencode(k.to_s) + '=' + urlencode(v.to_s) }.join('&') headers['Content-Type'] = 'application/x-www-form-urlencoded' end request(:post, url, headers, :data => data) end # Uploads the contents of a file to the specified +url+ using HTTP POST. def post_file(url, filename, headers = {}) request(:post, url, headers, :file => filename) end # Uploads the contents of a file and data to the specified +url+ using HTTP POST. def post_multipart(url, data, filename, headers = {}) request(:post, url, headers, {:data => data, :file => filename, :multipart => true}) end ################################################################### ### WebDAV methods ### # Sends a WebDAV COPY request to the specified +url+. def copy(url, dest, headers = {}) headers['Destination'] = dest request(:copy, url, headers) end ################################################################### ### Basic API methods ### # Send an HTTP request to the specified +url+. def request(action, url, headers, options = {}) # If the Expect header isn't set uploads are really slow headers['Expect'] ||= '' req = Request.new req.action = action req.headers = self.headers.merge headers req.timeout = options.fetch :timeout, self.timeout req.connect_timeout = options.fetch :connect_timeout, self.connect_timeout req.max_redirects = options.fetch :max_redirects, self.max_redirects req.username = options.fetch :username, self.username req.password = options.fetch :password, self.password req.proxy = options.fetch :proxy, self.proxy req.proxy_type = options.fetch :proxy_type, self.proxy_type req.auth_type = options.fetch :auth_type, self.auth_type req.insecure = options.fetch :insecure, self.insecure req.ignore_content_length = options.fetch :ignore_content_length, self.ignore_content_length req.buffer_size = options.fetch :buffer_size, self.buffer_size req.multipart = options[:multipart] req.upload_data = options[:data] req.file_name = options[:file] url = self.base_url.to_s + url.to_s uri = URI.parse(url) query = uri.query.to_s.split('&') query += options[:query].is_a?(Hash) ? Util.build_query_pairs_from_hash(options[:query]) : options[:query].to_s.split('&') uri.query = query.join('&') uri.query = nil if uri.query.empty? url = uri.to_s raise ArgumentError, "Empty URL" if url.empty? req.url = url handle_request(req) end end end ruby-patron-0.4.18/lib/patron/util.rb000066400000000000000000000036221204444370700174510ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Patron HTTP Client: Request class ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require 'cgi' module Patron module Util extend self def build_query_pairs_from_hash(hash, escape_values=false) pairs = [] recursive = Proc.new do |h, prefix| h.each_pair do |k,v| key = prefix == '' ? k : "#{prefix}[#{k}]" v = CGI::escape(v.to_s) if escape_values v.is_a?(Hash) ? recursive.call(v, key) : pairs << "#{key}=#{v}" end end recursive.call(hash, '') pairs end def build_query_string_from_hash(hash, escape_values=false) build_query_pairs_from_hash(hash, escape_values).join('&') end end endruby-patron-0.4.18/lib/patron/version.rb000066400000000000000000000000471204444370700201570ustar00rootroot00000000000000module Patron VERSION = "0.4.18" end ruby-patron-0.4.18/metadata.yml000066400000000000000000000054061204444370700164030ustar00rootroot00000000000000--- !ruby/object:Gem::Specification name: patron version: !ruby/object:Gem::Version version: 0.4.18 prerelease: platform: ruby authors: - Phillip Toland autorequire: bindir: bin cert_chain: [] date: 2012-03-05 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: bundler requirement: &70336820705120 !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: 1.0.0 type: :development prerelease: false version_requirements: *70336820705120 - !ruby/object:Gem::Dependency name: rake-compiler requirement: &70336820704660 !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: 0.7.5 type: :development prerelease: false version_requirements: *70336820704660 - !ruby/object:Gem::Dependency name: rspec requirement: &70336820704200 !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: 2.3.0 type: :development prerelease: false version_requirements: *70336820704200 - !ruby/object:Gem::Dependency name: rcov requirement: &70336820703740 !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: 0.9.9 type: :development prerelease: false version_requirements: *70336820703740 description: Ruby HTTP client library based on libcurl email: - phil.toland@gmail.com executables: [] extensions: - ext/patron/extconf.rb extra_rdoc_files: [] files: - .autotest - .gitignore - .rspec - Gemfile - Gemfile.lock - LICENSE - README.txt - Rakefile - ext/patron/.gitignore - ext/patron/extconf.rb - ext/patron/membuffer.c - ext/patron/membuffer.h - ext/patron/session_ext.c - ext/patron/sglib.h - lib/patron.rb - lib/patron/error.rb - lib/patron/proxy_type.rb - lib/patron/request.rb - lib/patron/response.rb - lib/patron/session.rb - lib/patron/util.rb - lib/patron/version.rb - patron.gemspec - pic.png - script/console - script/test_server - spec/patron_spec.rb - spec/request_spec.rb - spec/response_spec.rb - spec/session_spec.rb - spec/spec_helper.rb - spec/support/test_server.rb - spec/util_spec.rb homepage: https://github.com/toland/patron licenses: [] post_install_message: rdoc_options: [] require_paths: - lib - ext required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: 1.2.0 requirements: [] rubyforge_project: patron rubygems_version: 1.8.10 signing_key: specification_version: 3 summary: Patron HTTP Client test_files: [] ruby-patron-0.4.18/patron.gemspec000066400000000000000000000017061204444370700167470ustar00rootroot00000000000000# -*- encoding: utf-8 -*- require File.expand_path("../lib/patron/version", __FILE__) Gem::Specification.new do |s| s.name = "patron" s.version = Patron::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Phillip Toland"] s.email = ["phil.toland@gmail.com"] s.homepage = "https://github.com/toland/patron" s.summary = "Patron HTTP Client" s.description = "Ruby HTTP client library based on libcurl" s.required_rubygems_version = ">= 1.2.0" s.rubyforge_project = "patron" s.add_development_dependency "bundler", ">= 1.0.0" s.add_development_dependency "rake-compiler", ">= 0.7.5" s.add_development_dependency "rspec", ">= 2.3.0" s.add_development_dependency "rcov", ">= 0.9.9" s.files = `git ls-files`.split("\n") s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact s.require_paths = ["lib", "ext"] s.extensions = ["ext/patron/extconf.rb"] end ruby-patron-0.4.18/pic.png000066400000000000000000000016261204444370700153610ustar00rootroot00000000000000‰PNG  IHDR:~›UtEXtSoftwareAdobe ImageReadyqÉe<"iTXtXML:com.adobe.xmp 5,Æz IDATcø6_g€IEND®B`‚ruby-patron-0.4.18/script/000077500000000000000000000000001204444370700153775ustar00rootroot00000000000000ruby-patron-0.4.18/script/console000077500000000000000000000004401204444370700167650ustar00rootroot00000000000000#!/usr/bin/env ruby # File: script/console irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb' cwd = File.dirname(__FILE__) libs = " -I#{cwd + '/../lib'} -I#{cwd + '/../ext'}" libs << " -r patron -r irb/completion" puts "Loading Patron" exec "#{irb} #{libs} --simple-prompt" ruby-patron-0.4.18/script/test_server000077500000000000000000000027331204444370700176770ustar00rootroot00000000000000#!/usr/bin/env ruby ## ------------------------------------------------------------------- ## ## Patron HTTP Client: HTTP test server for integration tests ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- root = File.expand_path('../..', __FILE__) require File.join(root, %w[spec support test_server.rb]) PatronTestServer.start($stderr).join ruby-patron-0.4.18/spec/000077500000000000000000000000001204444370700150255ustar00rootroot00000000000000ruby-patron-0.4.18/spec/patron_spec.rb000066400000000000000000000032001204444370700176620ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require File.expand_path("./spec") + '/spec_helper.rb' describe Patron do it "should return the version number of the Patron library" do version = Patron.version version.should match(%r|^\d+.\d+.\d+$|) end it "should return the version number of the libcurl library" do version = Patron.libcurl_version version.should match(%r|^libcurl/\d+.\d+.\d+|) end end ruby-patron-0.4.18/spec/request_spec.rb000066400000000000000000000054331204444370700200610ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require File.expand_path("./spec") + '/spec_helper.rb' describe Patron::Request do before(:each) do @request = Patron::Request.new end describe :action do it "should accept :get, :put, :post, :delete and :head" do [:get, :put, :post, :delete, :head, :copy].each do |action| lambda {@request.action = action}.should_not raise_error end end it "should raise an exception when assigned a bad value" do lambda {@request.action = :foo}.should raise_error(ArgumentError) end end describe :timeout do it "should raise an exception when assigned a negative number" do lambda {@request.timeout = -1}.should raise_error(ArgumentError) end it "should raise an exception when assigned 0" do lambda {@request.timeout = 0}.should raise_error(ArgumentError) end end describe :max_redirects do it "should raise an error when assigned an integer smaller than -1" do lambda {@request.max_redirects = -2}.should raise_error(ArgumentError) end end describe :headers do it "should raise an error when assigned something other than a hash" do lambda {@request.headers = :foo}.should raise_error(ArgumentError) end end describe :buffer_size do it "should raise an exception when assigned a negative number" do lambda {@request.buffer_size = -1}.should raise_error(ArgumentError) end it "should raise an exception when assigned 0" do lambda {@request.buffer_size = 0}.should raise_error(ArgumentError) end end end ruby-patron-0.4.18/spec/response_spec.rb000066400000000000000000000044571204444370700202340ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Copyright (c) 2009 Phillip Toland ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require File.expand_path("./spec") + '/spec_helper.rb' require 'webrick' require 'base64' require 'fileutils' describe Patron::Response do before(:each) do @session = Patron::Session.new @session.base_url = "http://localhost:9001" end it "should strip extra spaces from header values" do response = @session.get("/test") # All digits, no spaces response.headers['Content-Length'].should match(/^\d+$/) end it "should return an array of values when multiple header fields have same name" do response = @session.get("/repetitiveheader") response.headers['Set-Cookie'].should == ["a=1","b=2"] end it "should works with non-text files" do response = @session.get("/picture") response.headers['Content-Type'].should == 'image/png' response.body.encoding.should == Encoding::ASCII_8BIT end it "should not allow a default charset to be nil" do Encoding.stub(:default_internal).and_return("UTF-8") expect { Patron::Response.new("url", "status", 0, "", "", nil) }.to_not raise_error end end ruby-patron-0.4.18/spec/session_spec.rb000066400000000000000000000245101204444370700200510ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require File.expand_path("./spec") + '/spec_helper.rb' require 'webrick' require 'yaml' require 'base64' require 'fileutils' describe Patron::Session do before(:each) do @session = Patron::Session.new @session.base_url = "http://localhost:9001" end it "should escape and unescape strings symetrically" do string = "foo~bar baz/" escaped = @session.escape(string) unescaped = @session.unescape(escaped) unescaped.should == string end it "should raise an error when passed an invalid action" do lambda { @session.request(:bogus, "/test", {}) }.should raise_error(ArgumentError) end it "should raise an error when no URL is provided" do @session.base_url = nil lambda {@session.get(nil)}.should raise_error(ArgumentError) end it "should retrieve a url with :get" do response = @session.get("/test") body = YAML::load(response.body) body.request_method.should == "GET" end it "should download content with :get and a file path" do tmpfile = "/tmp/patron_test.yaml" response = @session.get_file "/test", tmpfile response.body.should be_nil body = YAML::load_file(tmpfile) body.request_method.should == "GET" FileUtils.rm tmpfile end it "should include custom headers in a request" do response = @session.get("/test", {"User-Agent" => "PatronTest"}) body = YAML::load(response.body) body.header["user-agent"].should == ["PatronTest"] end it "should merge custom headers with session headers" do @session.headers["X-Test"] = "Testing" response = @session.get("/test", {"User-Agent" => "PatronTest"}) body = YAML::load(response.body) body.header["user-agent"].should == ["PatronTest"] body.header["x-test"].should == ["Testing"] end it "should raise an exception on timeout" do @session.timeout = 1 lambda {@session.get("/timeout")}.should raise_error(Patron::TimeoutError) end it "should follow redirects by default" do @session.max_redirects = 1 response = @session.get("/redirect") body = YAML::load(response.body) response.status.should == 200 body.path.should == "/test" end it "should include redirect count in response" do @session.max_redirects = 1 response = @session.get("/redirect") response.redirect_count.should == 1 end it "should not follow redirects when configured to do so" do @session.max_redirects = 0 response = @session.get("/redirect") response.status.should == 301 response.body.should be_empty end it "should retrieve URL metadata with :head" do response = @session.head("/test") response.status.should == 200 response.body.should be_empty response.headers.should_not be_empty end it "should send a delete request with :delete" do response = @session.delete("/test") body = YAML::load(response.body) body.request_method.should == "DELETE" end it "should send a COPY request with :copy" do response = @session.copy("/test", "/test2") body = YAML::load(response.body) body.request_method.should == "COPY" end it "should include a Destination header in COPY requests" do response = @session.copy("/test", "/test2") body = YAML::load(response.body) body.header['destination'].first.should == "/test2" end it "should upload data with :get" do data = "upload data" response = @session.request(:get, "/test", {}, :data => data) body = YAML::load(response.body) body.request_method.should == "GET" body.header['content-length'].should == [data.size.to_s] end it "should upload data with :put" do data = "upload data" response = @session.put("/test", data) body = YAML::load(response.body) body.request_method.should == "PUT" body.header['content-length'].should == [data.size.to_s] end it "should raise when no data is provided to :put" do lambda { @session.put("/test", nil) }.should raise_error(ArgumentError) end it "should upload a file with :put" do response = @session.put_file("/test", "LICENSE") body = YAML::load(response.body) body.request_method.should == "PUT" end it "should raise when no file is provided to :put" do lambda { @session.put_file("/test", nil) }.should raise_error(ArgumentError) end it "should use chunked encoding when uploading a file with :put" do response = @session.put_file("/test", "LICENSE") body = YAML::load(response.body) body.header['transfer-encoding'].first.should == "chunked" end it "should upload data with :post" do data = "upload data" response = @session.post("/test", data) body = YAML::load(response.body) body.request_method.should == "POST" body.header['content-length'].should == [data.size.to_s] end it "should post a hash of arguments as a urlencoded form" do data = {:foo => 123, 'baz' => '++hello world++'} response = @session.post("/testpost", data) body = YAML::load(response.body) body['content_type'].should == "application/x-www-form-urlencoded" body['body'].should match(/baz=%2B%2Bhello%20world%2B%2B/) body['body'].should match(/foo=123/) end it "should raise when no data is provided to :post" do lambda { @session.post("/test", nil) }.should raise_error(ArgumentError) end it "should upload a file with :post" do response = @session.post_file("/test", "LICENSE") body = YAML::load(response.body) body.request_method.should == "POST" end it "should upload a multipart with :post" do response = @session.post_multipart("/test", { :test_data => "123" }, { :test_file => "LICENSE" } ) body = YAML::load(response.body) body.request_method.should == "POST" end it "should raise when no file is provided to :post" do lambda { @session.post_file("/test", nil) }.should raise_error(ArgumentError) end it "should use chunked encoding when uploading a file with :post" do response = @session.post_file("/test", "LICENSE") body = YAML::load(response.body) body.header['transfer-encoding'].first.should == "chunked" end it "should pass credentials as http basic auth" do @session.username = "foo" @session.password = "bar" response = @session.get("/test") body = YAML::load(response.body) body.header['authorization'].should == [encode_authz("foo", "bar")] end it "should handle cookies if set" do @session.handle_cookies response = @session.get("/setcookie").body YAML::load(response).header['cookie'].first.should == "session_id=foo123" end it "should not handle cookies by default" do response = @session.get("/setcookie").body YAML::load(response).header.should_not include('cookie') end it "should ignore a wrong Content-Length when asked to" do lambda { @session.ignore_content_length = true @session.get("/wrongcontentlength") }.should_not raise_error end it "should fail by default with a Content-Length too high" do lambda { @session.ignore_content_length = nil @session.get("/wrongcontentlength") }.should raise_error(Patron::PartialFileError) end it "should raise exception if cookie store is not writable or readable" do lambda { @session.handle_cookies("/trash/clash/foo") }.should raise_error(ArgumentError) end it "should work with multiple threads" do threads = [] 3.times do threads << Thread.new do session = Patron::Session.new session.base_url = "http://localhost:9001" session.post_file("/test", "LICENSE") end end threads.each {|t| t.join } end it "should limit the buffer_size" do # Buffer size is tricky to test, as it only affects the buffer size for each # read and it's not really visible at this, higher level. It's also only a # suggestion rather than a command so it may not even take affect. Currently # we just test that the response completes without any issues, it would be nice # to have a more robust test here. @session.buffer_size = 1 body = nil lambda { response = @session.get("/test") body = YAML::load(response.body) }.should_not raise_error body.request_method.should == "GET" end it "should serialize query params and append them to the url" do response = @session.request(:get, "/test", {}, :query => {:foo => "bar"}) request = YAML::load(response.body) request.parse (request.path + '?' + request.query_string).should == "/test?foo=bar" end it "should merge parameters in the :query option with pre-existing query parameters" do response = @session.request(:get, "/test?foo=bar", {}, :query => {:baz => "quux"}) request = YAML::load(response.body) request.parse (request.path + '?' + request.query_string).should == "/test?foo=bar&baz=quux" end def encode_authz(user, passwd) "Basic " + Base64.encode64("#{user}:#{passwd}").strip end # ------------------------------------------------------------------------ describe 'when debug is enabled' do it 'it should not clobber stderr' do rdev = STDERR.stat.rdev @session.enable_debug STDERR.stat.rdev.should be == rdev @session.enable_debug STDERR.stat.rdev.should be == rdev end end end ruby-patron-0.4.18/spec/spec_helper.rb000066400000000000000000000027471204444370700176550ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require 'rspec' $:.unshift(File.dirname(__FILE__) + '/../lib') $:.unshift(File.dirname(__FILE__) + '/../ext') require 'patron' Dir['./spec/support/**/*.rb'].each { |fn| require fn } PatronTestServer.start if RUBY_VERSION >= '1.9' ruby-patron-0.4.18/spec/support/000077500000000000000000000000001204444370700165415ustar00rootroot00000000000000ruby-patron-0.4.18/spec/support/test_server.rb000066400000000000000000000112371204444370700214370ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Patron HTTP Client: HTTP test server for integration tests ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require 'yaml' require 'webrick' include WEBrick # This ugly little hack is necessary to make the specs pass when running # the test_server script under Ruby 1.9. URI::Parser#to_yaml generates # regexp representations that YAML.parse cannot parse. class URI::Parser def to_yaml(opts = {}) {}.to_yaml(opts) end end module RespondWith def respond_with(method, req, res) res.body = req.to_yaml res['Content-Type'] = "text/plain" end end class TestServlet < HTTPServlet::AbstractServlet include RespondWith def do_GET(req,res) respond_with(:GET, req, res) end def do_POST(req,res) respond_with(:POST, req, res) end def do_PUT(req,res) respond_with(:PUT, req, res) end def do_DELETE(req,res) respond_with(:DELETE, req, res) end def do_COPY(req,res) respond_with(:COPY, req, res) end end class TimeoutServlet < HTTPServlet::AbstractServlet def do_GET(req,res) sleep(1.1) end end class RedirectServlet < HTTPServlet::AbstractServlet def do_GET(req,res) res['Location'] = "http://localhost:9001/test" res.status = 301 end end class TestPostBodyServlet < HTTPServlet::AbstractServlet include RespondWith def do_POST(req, res) respond_with(:POST, {'body' => req.body, 'content_type' => req.content_type}, res) end end class SetCookieServlet < HTTPServlet::AbstractServlet def do_GET(req, res) res['Set-Cookie'] = "session_id=foo123" res['Location'] = "http://localhost:9001/test" res.status = 301 end end class RepetitiveHeaderServlet < HTTPServlet::AbstractServlet def do_GET(req, res) # the only way to get webrick to output two headers with the same name is using cookies, so that's what we'll do: res.cookies << Cookie.new('a', '1') res.cookies << Cookie.new('b', '2') res['Content-Type'] = "text/plain" res.body = "Hi." end end class PictureServlet < HTTPServlet::AbstractServlet def do_GET(req, res) res['Content-Type'] = "image/png" res.body = File.read("./pic.png") end end class WrongContentLengthServlet < HTTPServlet::AbstractServlet def do_GET(req, res) res.keep_alive = false res.content_length = 1024 res.body = "Hello." end end class PatronTestServer def self.start( log_file = nil ) new(log_file).start end def initialize( log_file = nil ) log_file ||= StringIO.new log = WEBrick::Log.new(log_file) @server = WEBrick::HTTPServer.new( :Port => 9001, :Logger => log, :AccessLog => [ [ log, WEBrick::AccessLog::COMMON_LOG_FORMAT ], [ log, WEBrick::AccessLog::REFERER_LOG_FORMAT ] ] ) @server.mount("/test", TestServlet) @server.mount("/testpost", TestPostBodyServlet) @server.mount("/timeout", TimeoutServlet) @server.mount("/redirect", RedirectServlet) @server.mount("/picture", PictureServlet) @server.mount("/setcookie", SetCookieServlet) @server.mount("/repetitiveheader", RepetitiveHeaderServlet) @server.mount("/wrongcontentlength", WrongContentLengthServlet) end def start trap('INT') { begin @server.shutdown unless @server.nil? rescue Object => e $stderr.puts "Error #{__FILE__}:#{__LINE__}\n#{e.message}" end } @thread = Thread.new { @server.start } Thread.pass self end def join if defined? @thread and @thread @thread.join end self end end ruby-patron-0.4.18/spec/util_spec.rb000066400000000000000000000062621204444370700173470ustar00rootroot00000000000000## ------------------------------------------------------------------- ## ## Copyright (c) 2008 The Hive http://www.thehive.com/ ## ## Permission is hereby granted, free of charge, to any person obtaining a copy ## of this software and associated documentation files (the "Software"), to deal ## in the Software without restriction, including without limitation the rights ## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ## copies of the Software, and to permit persons to whom the Software is ## furnished to do so, subject to the following conditions: ## ## The above copyright notice and this permission notice shall be included in ## all copies or substantial portions of the Software. ## ## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ## THE SOFTWARE. ## ## ------------------------------------------------------------------- require File.expand_path("./spec") + '/spec_helper.rb' describe Patron::Util do describe :build_query_pairs_from_hash do it "correctly serializes a simple hash" do hash = {:foo => "bar", "baz" => 42} array = Patron::Util.build_query_pairs_from_hash(hash) array.size.should == 2 array.should include("foo=bar") array.should include("baz=42") end it "correctly serializes a more complex hash" do hash = { :foo => "bar", :baz => { "quux" => { :zing => { :ying => 42 } }, :blargh => { :spaz => "sox", :razz => "matazz" } } } array = Patron::Util.build_query_pairs_from_hash(hash) array.size.should == 4 array.should include("foo=bar") array.should include("baz[quux][zing][ying]=42") array.should include("baz[blargh][spaz]=sox") array.should include("baz[blargh][razz]=matazz") end end describe :build_query_string_from_hash do it "correctly serializes a simple hash" do hash = {:foo => "bar", "baz" => 42} array = Patron::Util.build_query_string_from_hash(hash).split('&') array.size.should == 2 array.should include("foo=bar") array.should include("baz=42") end it "correctly serializes a more complex hash" do hash = { :foo => "bar", :baz => { "quux" => { :zing => { :ying => 42 } }, :blargh => { :spaz => "sox", :razz => "matazz" } } } array = Patron::Util.build_query_string_from_hash(hash).split('&') array.size.should == 4 array.should include("foo=bar") array.should include("baz[quux][zing][ying]=42") array.should include("baz[blargh][spaz]=sox") array.should include("baz[blargh][razz]=matazz") end end end