kcar 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +7 -0
- data/.gitignore +20 -0
- data/COPYING +339 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +197 -0
- data/LICENSE +55 -0
- data/README +86 -0
- data/Rakefile +149 -0
- data/TODO +6 -0
- data/ext/kcar/c_util.h +105 -0
- data/ext/kcar/ext_help.h +82 -0
- data/ext/kcar/extconf.rb +14 -0
- data/ext/kcar/kcar.rl +656 -0
- data/ext/kcar/kcar_http_common.rl +56 -0
- data/kcar.gemspec +40 -0
- data/lib/kcar.rb +11 -0
- data/lib/kcar/parser.rb +39 -0
- data/lib/kcar/response.rb +168 -0
- data/setup.rb +1586 -0
- data/test/test_parser.rb +257 -0
- data/test/test_response.rb +415 -0
- metadata +96 -0
data/LICENSE
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
kcar is copyrighted free software by all contributors, see logs in
|
2
|
+
revision control for names and email addresses of all of them. You can
|
3
|
+
redistribute it and/or modify it under either the terms of the
|
4
|
+
{GPL2}[http://www.gnu.org/licenses/gpl-2.0.txt] (see link:COPYING) or
|
5
|
+
the conditions below:
|
6
|
+
|
7
|
+
1. You may make and give away verbatim copies of the source form of the
|
8
|
+
software without restriction, provided that you duplicate all of the
|
9
|
+
original copyright notices and associated disclaimers.
|
10
|
+
|
11
|
+
2. You may modify your copy of the software in any way, provided that
|
12
|
+
you do at least ONE of the following:
|
13
|
+
|
14
|
+
a) place your modifications in the Public Domain or otherwise make them
|
15
|
+
Freely Available, such as by posting said modifications to Usenet or an
|
16
|
+
equivalent medium, or by allowing the author to include your
|
17
|
+
modifications in the software.
|
18
|
+
|
19
|
+
b) use the modified software only within your corporation or
|
20
|
+
organization.
|
21
|
+
|
22
|
+
c) rename any non-standard executables so the names do not conflict with
|
23
|
+
standard executables, which must also be provided.
|
24
|
+
|
25
|
+
d) make other distribution arrangements with the author.
|
26
|
+
|
27
|
+
3. You may distribute the software in object code or executable
|
28
|
+
form, provided that you do at least ONE of the following:
|
29
|
+
|
30
|
+
a) distribute the executables and library files of the software,
|
31
|
+
together with instructions (in the manual page or equivalent) on where
|
32
|
+
to get the original distribution.
|
33
|
+
|
34
|
+
b) accompany the distribution with the machine-readable source of the
|
35
|
+
software.
|
36
|
+
|
37
|
+
c) give non-standard executables non-standard names, with
|
38
|
+
instructions on where to get the original software distribution.
|
39
|
+
|
40
|
+
d) make other distribution arrangements with the author.
|
41
|
+
|
42
|
+
4. You may modify and include the part of the software into any other
|
43
|
+
software (possibly commercial). But some files in the distribution
|
44
|
+
are not written by the author, so that they are not under this terms.
|
45
|
+
|
46
|
+
5. The scripts and library files supplied as input to or produced as
|
47
|
+
output from the software do not automatically fall under the
|
48
|
+
copyright of the software, but belong to whomever generated them,
|
49
|
+
and may be sold commercially, and may be aggregated with this
|
50
|
+
software.
|
51
|
+
|
52
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
53
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
54
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
55
|
+
PURPOSE.
|
data/README
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
= kcar - bytestream to Rack response converter
|
2
|
+
|
3
|
+
kcar features an HTTP parser that will convert a bytestream into a
|
4
|
+
3-element array suitable for use as a Rack response. It is IO interface
|
5
|
+
agnostic, so it may be used with HTTP streams over Unix domain sockets,
|
6
|
+
regular files, FIFOs, StringIOs as well as traditional TCP sockets.
|
7
|
+
|
8
|
+
A drop-in, Net::HTTP-compatible interface is planned.
|
9
|
+
|
10
|
+
== Features
|
11
|
+
|
12
|
+
* RFC2616-compliant Ragel+C parser adapted from Unicorn and Mongrel
|
13
|
+
|
14
|
+
* decodes chunked response bodies with an optional pass-through mode
|
15
|
+
(to avoid rechunking with Rack::Chunked)
|
16
|
+
|
17
|
+
* handles odd things like trailers and multiline headers
|
18
|
+
|
19
|
+
* streaming interface for response bodies allows for incremental
|
20
|
+
processing of arbitrarily large responses.
|
21
|
+
|
22
|
+
== Problems
|
23
|
+
|
24
|
+
* kcar is only lightly tested and is not yet aware of all quirks found in
|
25
|
+
all real (possibly broken) web servers.
|
26
|
+
|
27
|
+
== Install
|
28
|
+
|
29
|
+
If you're using a packaged Ruby distribution, make sure you have a C
|
30
|
+
compiler and the matching Ruby development libraries and headers.
|
31
|
+
|
32
|
+
If you use RubyGems:
|
33
|
+
|
34
|
+
gem install kcar
|
35
|
+
|
36
|
+
Otherwise grab the latest tarball from:
|
37
|
+
|
38
|
+
http://bogomips.org/kcar/files/
|
39
|
+
|
40
|
+
Unpack it, and run "ruby setup.rb"
|
41
|
+
|
42
|
+
== Usage:
|
43
|
+
|
44
|
+
While you can use the Kcar::Parser directly, you'll usually want the
|
45
|
+
higher-level interface of Kcar::Response:
|
46
|
+
|
47
|
+
require 'rack' # for Rack::Utils::HeaderHash, which is optional
|
48
|
+
require 'socket' # for TCPSocket
|
49
|
+
require 'kcar'
|
50
|
+
sock = TCPSocket.new('example.com', 80)
|
51
|
+
sock.write("GET / HTTP/1.0\r\n\r\n")
|
52
|
+
|
53
|
+
# instead of a Rack::Utils::HeaderHash object below, you can also
|
54
|
+
# pass a regular Hash or Array object.
|
55
|
+
response = Kcar::Response.new(sock, Rack::Utils::HeaderHash.new)
|
56
|
+
status, headers, body = response.rack
|
57
|
+
|
58
|
+
You can now do further processing on the status, headers, or iterate
|
59
|
+
through the body with body.each.
|
60
|
+
|
61
|
+
== Development
|
62
|
+
|
63
|
+
You can get the latest source via git from the following locations:
|
64
|
+
|
65
|
+
git://git.bogomips.org/kcar.git
|
66
|
+
git://repo.or.cz/kcar.git (mirror)
|
67
|
+
|
68
|
+
You may browse the code from the web and download the latest snapshot
|
69
|
+
tarballs here:
|
70
|
+
|
71
|
+
* http://git.bogomips.org/cgit/kcar.git (cgit)
|
72
|
+
* http://repo.or.cz/w/kcar.git (gitweb)
|
73
|
+
|
74
|
+
Inline patches (from "git format-patch") to the mailing list are
|
75
|
+
preferred because they allow code review and comments in the reply to
|
76
|
+
the patch.
|
77
|
+
|
78
|
+
We will adhere to mostly the same conventions for patch submissions as
|
79
|
+
git itself. See the Documentation/SubmittingPatches document
|
80
|
+
distributed with git on on patch submission guidelines to follow. Just
|
81
|
+
don't email the git mailing list or maintainer with kcar patches.
|
82
|
+
|
83
|
+
== Contact
|
84
|
+
|
85
|
+
All feedback (bug reports, user/development discussion, patches, pull
|
86
|
+
requests) go to the mailing list: mailto:kcar@librelist.com
|
data/Rakefile
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
desc "read news article from STDIN and post to rubyforge"
|
2
|
+
task :publish_news do
|
3
|
+
require 'rubyforge'
|
4
|
+
IO.select([STDIN], nil, nil, 1) or abort "E: news must be read from stdin"
|
5
|
+
msg = STDIN.readlines
|
6
|
+
subject = msg.shift
|
7
|
+
blank = msg.shift
|
8
|
+
blank == "\n" or abort "no newline after subject!"
|
9
|
+
subject.strip!
|
10
|
+
body = msg.join("").strip!
|
11
|
+
|
12
|
+
rf = RubyForge.new.configure
|
13
|
+
rf.login
|
14
|
+
rf.post_news('rainbows', subject, body)
|
15
|
+
end
|
16
|
+
|
17
|
+
def tags
|
18
|
+
timefmt = '%Y-%m-%dT%H:%M:%SZ'
|
19
|
+
@tags ||= `git tag -l`.split(/\n/).map do |tag|
|
20
|
+
next if tag == "v0.0.0"
|
21
|
+
if %r{\Av[\d\.]+\z} =~ tag
|
22
|
+
header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
|
23
|
+
header = header.split(/\n/)
|
24
|
+
tagger = header.grep(/\Atagger /).first
|
25
|
+
body ||= "initial"
|
26
|
+
{
|
27
|
+
:time => Time.at(tagger.split(/ /)[-2].to_i).utc.strftime(timefmt),
|
28
|
+
:tagger_name => %r{^tagger ([^<]+)}.match(tagger)[1],
|
29
|
+
:tagger_email => %r{<([^>]+)>}.match(tagger)[1],
|
30
|
+
:id => `git rev-parse refs/tags/#{tag}`.chomp!,
|
31
|
+
:tag => tag,
|
32
|
+
:subject => subject,
|
33
|
+
:body => body,
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end.compact.sort { |a,b| b[:time] <=> a[:time] }
|
37
|
+
end
|
38
|
+
|
39
|
+
cgit_url = "http://git.bogomips.org/cgit/kcar.git"
|
40
|
+
git_url = ENV['GIT_URL'] || 'git://git.bogomips.org/kcar.git'
|
41
|
+
|
42
|
+
desc 'prints news as an Atom feed'
|
43
|
+
task :news_atom do
|
44
|
+
require 'nokogiri'
|
45
|
+
new_tags = tags[0,10]
|
46
|
+
puts(Nokogiri::XML::Builder.new do
|
47
|
+
feed :xmlns => "http://www.w3.org/2005/Atom" do
|
48
|
+
id! "http://kcar.rubyforge.org/NEWS.atom.xml"
|
49
|
+
title "Kcar news"
|
50
|
+
subtitle File.readlines("README").first
|
51
|
+
link! :rel => 'alternate', :type => 'text/html',
|
52
|
+
:href => 'http://bogomips.org/kcar/'
|
53
|
+
updated( (new_tags.first[:time] rescue nil) || Time.now )
|
54
|
+
new_tags.each do |tag|
|
55
|
+
entry do
|
56
|
+
title tag[:subject]
|
57
|
+
updated tag[:time]
|
58
|
+
published tag[:time]
|
59
|
+
author {
|
60
|
+
name tag[:tagger_name]
|
61
|
+
email tag[:tagger_email]
|
62
|
+
}
|
63
|
+
url = "#{cgit_url}/tag/?id=#{tag[:tag]}"
|
64
|
+
link! :rel => "alternate", :type => "text/html", :href =>url
|
65
|
+
id! url
|
66
|
+
content(:type => 'text') { tag[:body] }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end.to_xml)
|
71
|
+
end
|
72
|
+
|
73
|
+
desc 'prints RDoc-formatted news'
|
74
|
+
task :news_rdoc do
|
75
|
+
tags.each do |tag|
|
76
|
+
time = tag[:time].tr!('T', ' ').gsub!(/:\d\dZ/, ' UTC')
|
77
|
+
puts "=== #{tag[:tag].sub(/^v/, '')} / #{time}"
|
78
|
+
puts ""
|
79
|
+
|
80
|
+
body = tag[:body]
|
81
|
+
puts tag[:body].gsub(/^/sm, " ").gsub(/[ \t]+$/sm, "")
|
82
|
+
puts ""
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
desc "print release changelog for Rubyforge"
|
87
|
+
task :release_changes do
|
88
|
+
version = ENV['VERSION'] or abort "VERSION= needed"
|
89
|
+
version = "v#{version}"
|
90
|
+
vtags = tags.map { |tag| tag[:tag] =~ /\Av/ and tag[:tag] }.sort
|
91
|
+
prev = vtags[vtags.index(version) - 1]
|
92
|
+
system('git', 'diff', '--stat', prev, version) or abort $?
|
93
|
+
puts ""
|
94
|
+
system('git', 'log', "#{prev}..#{version}") or abort $?
|
95
|
+
end
|
96
|
+
|
97
|
+
desc "print release notes for Rubyforge"
|
98
|
+
task :release_notes do
|
99
|
+
require 'rubygems'
|
100
|
+
|
101
|
+
spec = Gem::Specification.load('kcar.gemspec')
|
102
|
+
puts spec.description.strip
|
103
|
+
puts ""
|
104
|
+
puts "* #{spec.homepage}"
|
105
|
+
puts "* #{spec.email}"
|
106
|
+
puts "* #{git_url}"
|
107
|
+
|
108
|
+
_, _, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
|
109
|
+
print "\nChanges:\n\n"
|
110
|
+
puts body
|
111
|
+
end
|
112
|
+
|
113
|
+
desc "post to RAA"
|
114
|
+
task :raa_update do
|
115
|
+
require 'rubygems'
|
116
|
+
require 'net/http'
|
117
|
+
require 'net/netrc'
|
118
|
+
rc = Net::Netrc.locate('kcar-raa') or abort "~/.netrc not found"
|
119
|
+
password = rc.password
|
120
|
+
|
121
|
+
s = Gem::Specification.load('kcar.gemspec')
|
122
|
+
desc = [ s.description.strip ]
|
123
|
+
desc << ""
|
124
|
+
desc << "* #{s.email}"
|
125
|
+
desc << "* #{git_url}"
|
126
|
+
desc << "* #{cgit_url}"
|
127
|
+
desc = desc.join("\n")
|
128
|
+
uri = URI.parse('http://raa.ruby-lang.org/regist.rhtml')
|
129
|
+
form = {
|
130
|
+
:name => s.name,
|
131
|
+
:short_description => s.summary,
|
132
|
+
:version => s.version.to_s,
|
133
|
+
:status => 'experimental',
|
134
|
+
:owner => s.authors.first,
|
135
|
+
:email => s.email,
|
136
|
+
:category_major => 'Library',
|
137
|
+
:category_minor => 'WWW',
|
138
|
+
:url => s.homepage,
|
139
|
+
:download => 'http://rubyforge.org/frs/?group_id=8977',
|
140
|
+
:license => "Ruby's",
|
141
|
+
:description_style => 'Plain',
|
142
|
+
:description => desc,
|
143
|
+
:pass => password,
|
144
|
+
:submit => 'Update',
|
145
|
+
}
|
146
|
+
res = Net::HTTP.post_form(uri, form)
|
147
|
+
p res
|
148
|
+
puts res.body
|
149
|
+
end
|
data/TODO
ADDED
data/ext/kcar/c_util.h
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
/*
|
2
|
+
* Generic C functions and macros go here, there are no dependencies
|
3
|
+
* on Unicorn internal structures or the Ruby C API in here.
|
4
|
+
*/
|
5
|
+
|
6
|
+
#ifndef UH_util_h
|
7
|
+
#define UH_util_h
|
8
|
+
|
9
|
+
#include <unistd.h>
|
10
|
+
#include <assert.h>
|
11
|
+
|
12
|
+
#define MIN(a,b) (a < b ? a : b)
|
13
|
+
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
14
|
+
|
15
|
+
#ifndef SIZEOF_OFF_T
|
16
|
+
# define SIZEOF_OFF_T 4
|
17
|
+
# warning SIZEOF_OFF_T not defined, guessing 4. Did you run extconf.rb?
|
18
|
+
#endif
|
19
|
+
|
20
|
+
#if SIZEOF_OFF_T == 4
|
21
|
+
# define UH_OFF_T_MAX 0x7fffffff
|
22
|
+
#elif SIZEOF_OFF_T == 8
|
23
|
+
# if SIZEOF_LONG == 4
|
24
|
+
# define UH_OFF_T_MAX 0x7fffffffffffffffLL
|
25
|
+
# else
|
26
|
+
# define UH_OFF_T_MAX 0x7fffffffffffffff
|
27
|
+
# endif
|
28
|
+
#else
|
29
|
+
# error off_t size unknown for this platform!
|
30
|
+
#endif /* SIZEOF_OFF_T check */
|
31
|
+
|
32
|
+
/*
|
33
|
+
* ragel enforces fpc as a const, and merely casting can make picky
|
34
|
+
* compilers unhappy, so we have this little helper do our dirty work
|
35
|
+
*/
|
36
|
+
static inline void *deconst(const void *in)
|
37
|
+
{
|
38
|
+
union { const void *in; void *out; } tmp;
|
39
|
+
|
40
|
+
tmp.in = in;
|
41
|
+
|
42
|
+
return tmp.out;
|
43
|
+
}
|
44
|
+
|
45
|
+
static int hexchar2int(int xdigit)
|
46
|
+
{
|
47
|
+
if (xdigit >= 'A' && xdigit <= 'F')
|
48
|
+
return xdigit - 'A' + 10;
|
49
|
+
if (xdigit >= 'a' && xdigit <= 'f')
|
50
|
+
return xdigit - 'a' + 10;
|
51
|
+
|
52
|
+
/* Ragel already does runtime range checking for us in Unicorn: */
|
53
|
+
assert(xdigit >= '0' && xdigit <= '9' && "invalid digit character");
|
54
|
+
|
55
|
+
return xdigit - '0';
|
56
|
+
}
|
57
|
+
|
58
|
+
/*
|
59
|
+
* multiplies +i+ by +base+ and increments the result by the parsed
|
60
|
+
* integer value of +xdigit+. +xdigit+ is a character byte
|
61
|
+
* representing a number the range of 0..(base-1)
|
62
|
+
* returns the new value of +i+ on success
|
63
|
+
* returns -1 on errors (including overflow)
|
64
|
+
*/
|
65
|
+
static off_t step_incr(off_t i, int xdigit, const int base)
|
66
|
+
{
|
67
|
+
static const off_t max = UH_OFF_T_MAX;
|
68
|
+
const off_t next_max = (max - (max % base)) / base;
|
69
|
+
off_t offset = hexchar2int(xdigit);
|
70
|
+
|
71
|
+
if (offset > (base - 1))
|
72
|
+
return -1;
|
73
|
+
if (i > next_max)
|
74
|
+
return -1;
|
75
|
+
i *= base;
|
76
|
+
|
77
|
+
if ((offset > (base - 1)) || ((max - i) < offset))
|
78
|
+
return -1;
|
79
|
+
|
80
|
+
return i + offset;
|
81
|
+
}
|
82
|
+
|
83
|
+
/*
|
84
|
+
* parses a non-negative length according to base-10 and
|
85
|
+
* returns it as an off_t value. Returns -1 on errors
|
86
|
+
* (including overflow).
|
87
|
+
*/
|
88
|
+
static off_t parse_length(const char *value, size_t length)
|
89
|
+
{
|
90
|
+
off_t rv;
|
91
|
+
|
92
|
+
for (rv = 0; length-- && rv >= 0; ++value) {
|
93
|
+
if (*value >= '0' && *value <= '9')
|
94
|
+
rv = step_incr(rv, *value, 10);
|
95
|
+
else
|
96
|
+
return -1;
|
97
|
+
}
|
98
|
+
|
99
|
+
return rv;
|
100
|
+
}
|
101
|
+
|
102
|
+
#define CONST_MEM_EQ(const_p, buf, len) \
|
103
|
+
((sizeof(const_p) - 1) == len && !memcmp(const_p, buf, sizeof(const_p) - 1))
|
104
|
+
|
105
|
+
#endif /* UH_util_h */
|
data/ext/kcar/ext_help.h
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#ifndef ext_help_h
|
2
|
+
#define ext_help_h
|
3
|
+
|
4
|
+
#ifndef RSTRING_PTR
|
5
|
+
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
6
|
+
#endif /* !defined(RSTRING_PTR) */
|
7
|
+
#ifndef RSTRING_LEN
|
8
|
+
#define RSTRING_LEN(s) (RSTRING(s)->len)
|
9
|
+
#endif /* !defined(RSTRING_LEN) */
|
10
|
+
|
11
|
+
#ifndef RUBINIUS
|
12
|
+
# define rb_str_update(x) do {} while (0)
|
13
|
+
# define rb_str_flush(x) do {} while (0)
|
14
|
+
#endif /* !RUBINIUS */
|
15
|
+
|
16
|
+
#ifndef HAVE_RB_STR_SET_LEN
|
17
|
+
# ifdef RUBINIUS
|
18
|
+
# define rb_str_set_len(str,len) rb_str_resize(str,len)
|
19
|
+
# else /* 1.8.6 optimized version */
|
20
|
+
/* this is taken from Ruby 1.8.7, 1.8.6 may not have it */
|
21
|
+
static void rb_18_str_set_len(VALUE str, long len)
|
22
|
+
{
|
23
|
+
RSTRING(str)->len = len;
|
24
|
+
RSTRING(str)->ptr[len] = '\0';
|
25
|
+
rb_str_flush(str);
|
26
|
+
}
|
27
|
+
# define rb_str_set_len(str,len) rb_18_str_set_len(str,len)
|
28
|
+
# endif /* ! RUBINIUS */
|
29
|
+
#endif /* !defined(HAVE_RB_STR_SET_LEN) */
|
30
|
+
|
31
|
+
/* not all Ruby implementations support frozen objects (Rubinius does not) */
|
32
|
+
#if defined(OBJ_FROZEN)
|
33
|
+
# define assert_frozen(f) assert(OBJ_FROZEN(f) && "unfrozen object")
|
34
|
+
#else
|
35
|
+
# define assert_frozen(f) do {} while (0)
|
36
|
+
#endif /* !defined(OBJ_FROZEN) */
|
37
|
+
|
38
|
+
#if !defined(OFFT2NUM)
|
39
|
+
# if SIZEOF_OFF_T == SIZEOF_LONG
|
40
|
+
# define OFFT2NUM(n) LONG2NUM(n)
|
41
|
+
# else
|
42
|
+
# define OFFT2NUM(n) LL2NUM(n)
|
43
|
+
# endif
|
44
|
+
#endif /* ! defined(OFFT2NUM) */
|
45
|
+
|
46
|
+
#ifndef HAVE_RB_STR_MODIFY
|
47
|
+
# define rb_str_modify(x) do {} while (0)
|
48
|
+
#endif /* ! defined(HAVE_RB_STR_MODIFY) */
|
49
|
+
|
50
|
+
static inline int str_cstr_eq(VALUE val, const char *ptr, long len)
|
51
|
+
{
|
52
|
+
return (RSTRING_LEN(val) == len && !memcmp(ptr, RSTRING_PTR(val), len));
|
53
|
+
}
|
54
|
+
|
55
|
+
#define STR_CSTR_EQ(val, const_str) \
|
56
|
+
str_cstr_eq(val, const_str, sizeof(const_str) - 1)
|
57
|
+
|
58
|
+
static int cstr_case_eq(const char *a, long alen, const char *b, long blen)
|
59
|
+
{
|
60
|
+
if (alen == blen) {
|
61
|
+
for (; blen--; ++a, ++b) {
|
62
|
+
if ((*a == *b) || ((*a >= 'A' && *a <= 'Z') && (*a | 0x20) == *b))
|
63
|
+
continue;
|
64
|
+
return 0;
|
65
|
+
}
|
66
|
+
return 1;
|
67
|
+
}
|
68
|
+
return 0;
|
69
|
+
}
|
70
|
+
|
71
|
+
/* strcasecmp isn't locale independent */
|
72
|
+
static int str_cstr_case_eq(VALUE val, const char *ptr, long len)
|
73
|
+
{
|
74
|
+
return cstr_case_eq(RSTRING_PTR(val), RSTRING_LEN(val), ptr, len);
|
75
|
+
}
|
76
|
+
|
77
|
+
#define STR_CSTR_CASE_EQ(val, const_str) \
|
78
|
+
str_cstr_case_eq(val, const_str, sizeof(const_str) - 1)
|
79
|
+
#define CSTR_CASE_EQ(ptr, len, const_str) \
|
80
|
+
cstr_case_eq(ptr, len, const_str, sizeof(const_str) - 1)
|
81
|
+
|
82
|
+
#endif /* ext_help_h */
|