io-extra 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/{CHANGES → CHANGES.md} +22 -17
- data/Gemfile +2 -0
- data/LICENSE +177 -0
- data/{MANIFEST → MANIFEST.md} +4 -4
- data/README.md +114 -0
- data/Rakefile +102 -0
- data/certs/djberg96_pub.pem +26 -0
- data/doc/io_extra.txt +85 -0
- data/examples/example_io_extra.rb +53 -0
- data/examples/example_pread.rb +24 -0
- data/examples/writev_benchmark.rb +19 -0
- data/ext/extra.bundle.dSYM/Contents/Info.plist +20 -0
- data/ext/extra.bundle.dSYM/Contents/Resources/Relocations/aarch64/extra.bundle.yml +5 -0
- data/ext/io/extra.c +172 -81
- data/io-extra.gemspec +51 -0
- data/lib/io-extra.rb +3 -1
- data/spec/io_extra_spec.rb +179 -0
- data.tar.gz.sig +0 -0
- metadata +74 -15
- metadata.gz.sig +0 -0
- data/README +0 -97
- data/test/test_io_extra.rb +0 -148
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
##############################################################################
|
|
2
|
+
# example_io_extra.rb
|
|
3
|
+
#
|
|
4
|
+
# This is a small example program for the io-extra library. Modify as you see
|
|
5
|
+
# fit. You can run this via the 'rake example' task.
|
|
6
|
+
##############################################################################
|
|
7
|
+
require_relative "../lib/io-extra"
|
|
8
|
+
puts "VERSION: #{IO::EXTRA_VERSION}"
|
|
9
|
+
|
|
10
|
+
begin
|
|
11
|
+
fh = File.open("foo.txt","w+")
|
|
12
|
+
|
|
13
|
+
puts "DIRECTIO"
|
|
14
|
+
sleep 2
|
|
15
|
+
|
|
16
|
+
p fh.directio?
|
|
17
|
+
fh.directio = IO::DIRECTIO_ON
|
|
18
|
+
p fh.directio?
|
|
19
|
+
|
|
20
|
+
puts "FDWALK"
|
|
21
|
+
sleep 2
|
|
22
|
+
|
|
23
|
+
IO.fdwalk(0){ |handle|
|
|
24
|
+
p handle
|
|
25
|
+
p handle.fileno
|
|
26
|
+
puts
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
=begin
|
|
30
|
+
STDIN.close
|
|
31
|
+
|
|
32
|
+
# Should print "Hello" 2 times
|
|
33
|
+
IO.fdwalk(0){ |fd|
|
|
34
|
+
puts "Hello #{fd}"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
IO.closefrom(0)
|
|
39
|
+
|
|
40
|
+
puts "Done" # Shouldn't see this
|
|
41
|
+
=end
|
|
42
|
+
|
|
43
|
+
puts "IO.writev"
|
|
44
|
+
sleep 2
|
|
45
|
+
|
|
46
|
+
a = (1..1000).map(&:to_s)
|
|
47
|
+
IO.writev(fh, a)
|
|
48
|
+
fh.rewind
|
|
49
|
+
p fh.read
|
|
50
|
+
ensure
|
|
51
|
+
fh.close
|
|
52
|
+
File.delete("foo.txt") if File.exist?("foo.txt")
|
|
53
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
########################################################################
|
|
2
|
+
# example_pread.rb
|
|
3
|
+
#
|
|
4
|
+
# Example program demonstrating the use of IO.pread.
|
|
5
|
+
########################################################################
|
|
6
|
+
require_relative '../lib/io-extra'
|
|
7
|
+
require 'tmpdir'
|
|
8
|
+
|
|
9
|
+
# Create a temporary file with a little data in it.
|
|
10
|
+
file = File.join(Dir.tmpdir, 'pread_test.txt')
|
|
11
|
+
File.open(file, 'w'){ |fh| 100.times{ |n| fh.puts "Hello: #{n}" } }
|
|
12
|
+
|
|
13
|
+
# Read from the file using pread.
|
|
14
|
+
begin
|
|
15
|
+
fh = File.open(file)
|
|
16
|
+
|
|
17
|
+
puts "Handle position before read: #{fh.pos}"
|
|
18
|
+
puts IO.pread(fh, 18, 0)
|
|
19
|
+
|
|
20
|
+
puts "Handle position after read: #{fh.pos}"
|
|
21
|
+
puts IO.pread(fh, 18, 0)
|
|
22
|
+
ensure
|
|
23
|
+
fh.close
|
|
24
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'benchmark'
|
|
2
|
+
require 'io-extra'
|
|
3
|
+
|
|
4
|
+
a = (0..1023).to_a.map(&:to_s)
|
|
5
|
+
|
|
6
|
+
file1 = 'write_test.txt'
|
|
7
|
+
file2 = 'writev_test.txt'
|
|
8
|
+
|
|
9
|
+
fh = File.open(file2, 'w')
|
|
10
|
+
|
|
11
|
+
Benchmark.bm(25) do |x|
|
|
12
|
+
x.report('write'){ 100000.times{ File.write(file1, a.join) } }
|
|
13
|
+
x.report('writev'){ 100000.times{ IO.writev(fh.fileno, a) } }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
fh.close
|
|
17
|
+
|
|
18
|
+
File.delete(file1)
|
|
19
|
+
File.delete(file2)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>CFBundleDevelopmentRegion</key>
|
|
6
|
+
<string>English</string>
|
|
7
|
+
<key>CFBundleIdentifier</key>
|
|
8
|
+
<string>com.apple.xcode.dsym.extra.bundle</string>
|
|
9
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
10
|
+
<string>6.0</string>
|
|
11
|
+
<key>CFBundlePackageType</key>
|
|
12
|
+
<string>dSYM</string>
|
|
13
|
+
<key>CFBundleSignature</key>
|
|
14
|
+
<string>????</string>
|
|
15
|
+
<key>CFBundleShortVersionString</key>
|
|
16
|
+
<string>1.0</string>
|
|
17
|
+
<key>CFBundleVersion</key>
|
|
18
|
+
<string>1</string>
|
|
19
|
+
</dict>
|
|
20
|
+
</plist>
|
data/ext/io/extra.c
CHANGED
|
@@ -87,9 +87,18 @@ static int open_max(void){
|
|
|
87
87
|
*/
|
|
88
88
|
static VALUE io_closefrom(VALUE klass, VALUE v_low_fd){
|
|
89
89
|
int i, lowfd;
|
|
90
|
-
int maxfd
|
|
90
|
+
int maxfd;
|
|
91
|
+
|
|
91
92
|
lowfd = NUM2INT(v_low_fd);
|
|
92
93
|
|
|
94
|
+
if(lowfd < 0)
|
|
95
|
+
rb_raise(rb_eArgError, "lowfd must be non-negative");
|
|
96
|
+
|
|
97
|
+
maxfd = open_max();
|
|
98
|
+
|
|
99
|
+
if(maxfd < 0)
|
|
100
|
+
rb_raise(rb_eRuntimeError, "failed to determine maximum file descriptor");
|
|
101
|
+
|
|
93
102
|
for(i = lowfd; i < maxfd; i++) {
|
|
94
103
|
if(!RB_RESERVED_FD_P(i))
|
|
95
104
|
close(i);
|
|
@@ -99,54 +108,7 @@ static VALUE io_closefrom(VALUE klass, VALUE v_low_fd){
|
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
#ifndef HAVE_FDWALK
|
|
102
|
-
|
|
103
|
-
int rv = 0;
|
|
104
|
-
int fd;
|
|
105
|
-
|
|
106
|
-
#ifdef PROC_SELF_FD_DIR
|
|
107
|
-
DIR *dir = opendir(PROC_SELF_FD_DIR);
|
|
108
|
-
|
|
109
|
-
if(dir){ /* procfs may not be mounted... */
|
|
110
|
-
struct dirent *ent;
|
|
111
|
-
int saved_errno;
|
|
112
|
-
int dir_fd = dirfd(dir);
|
|
113
|
-
|
|
114
|
-
while((ent = readdir(dir))){
|
|
115
|
-
char *err = NULL;
|
|
116
|
-
|
|
117
|
-
if(ent->d_name[0] == '.')
|
|
118
|
-
continue;
|
|
119
|
-
|
|
120
|
-
errno = 0;
|
|
121
|
-
fd = (int)strtol(ent->d_name, &err, 10);
|
|
122
|
-
|
|
123
|
-
if (errno || ! err || *err || fd == dir_fd)
|
|
124
|
-
continue;
|
|
125
|
-
|
|
126
|
-
if ((rv = func(data, fd)))
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
saved_errno = errno;
|
|
130
|
-
closedir(dir);
|
|
131
|
-
errno = saved_errno;
|
|
132
|
-
} else
|
|
133
|
-
#endif /* PROC_SELF_FD_DIR */
|
|
134
|
-
{
|
|
135
|
-
int maxfd = open_max();
|
|
136
|
-
|
|
137
|
-
for(fd = 0; fd < maxfd; fd++){
|
|
138
|
-
/* use fcntl to detect whether fd is a valid file descriptor */
|
|
139
|
-
errno = 0;
|
|
140
|
-
if(fcntl(fd, F_GETFD) < 0)
|
|
141
|
-
continue;
|
|
142
|
-
|
|
143
|
-
errno = 0;
|
|
144
|
-
if ((rv = func(data, fd)))
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return rv;
|
|
149
|
-
}
|
|
111
|
+
/* Define HAVE_FDWALK so the io_fdwalk implementation below is compiled */
|
|
150
112
|
#define HAVE_FDWALK
|
|
151
113
|
#endif
|
|
152
114
|
|
|
@@ -169,6 +131,74 @@ static int close_func(void* lowfd, int fd){
|
|
|
169
131
|
return 0;
|
|
170
132
|
}
|
|
171
133
|
|
|
134
|
+
/* Structure to pass data to fdwalk_body and fdwalk_ensure */
|
|
135
|
+
struct fdwalk_data {
|
|
136
|
+
int lowfd;
|
|
137
|
+
#ifdef PROC_SELF_FD_DIR
|
|
138
|
+
DIR *dir;
|
|
139
|
+
#endif
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/* Cleanup function for rb_ensure */
|
|
143
|
+
static VALUE fdwalk_ensure(VALUE arg){
|
|
144
|
+
struct fdwalk_data *data = (struct fdwalk_data *)arg;
|
|
145
|
+
#ifdef PROC_SELF_FD_DIR
|
|
146
|
+
if(data->dir){
|
|
147
|
+
closedir(data->dir);
|
|
148
|
+
data->dir = NULL;
|
|
149
|
+
}
|
|
150
|
+
#endif
|
|
151
|
+
(void)data; /* Suppress unused warning when PROC_SELF_FD_DIR not defined */
|
|
152
|
+
return Qnil;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* Main fdwalk iteration body */
|
|
156
|
+
static VALUE fdwalk_body(VALUE arg){
|
|
157
|
+
struct fdwalk_data *data = (struct fdwalk_data *)arg;
|
|
158
|
+
int fd;
|
|
159
|
+
|
|
160
|
+
#ifdef PROC_SELF_FD_DIR
|
|
161
|
+
if(data->dir){
|
|
162
|
+
struct dirent *ent;
|
|
163
|
+
int dir_fd = dirfd(data->dir);
|
|
164
|
+
|
|
165
|
+
while((ent = readdir(data->dir))){
|
|
166
|
+
char *err = NULL;
|
|
167
|
+
|
|
168
|
+
if(ent->d_name[0] == '.')
|
|
169
|
+
continue;
|
|
170
|
+
|
|
171
|
+
errno = 0;
|
|
172
|
+
fd = (int)strtol(ent->d_name, &err, 10);
|
|
173
|
+
|
|
174
|
+
if (errno || !err || *err || fd == dir_fd)
|
|
175
|
+
continue;
|
|
176
|
+
|
|
177
|
+
/* Validate fd is still open before calling callback (reduces race window) */
|
|
178
|
+
if(fcntl(fd, F_GETFD) < 0)
|
|
179
|
+
continue;
|
|
180
|
+
|
|
181
|
+
close_func(&data->lowfd, fd);
|
|
182
|
+
}
|
|
183
|
+
} else
|
|
184
|
+
#endif /* PROC_SELF_FD_DIR */
|
|
185
|
+
{
|
|
186
|
+
int maxfd = open_max();
|
|
187
|
+
|
|
188
|
+
for(fd = 0; fd < maxfd; fd++){
|
|
189
|
+
/* use fcntl to detect whether fd is a valid file descriptor */
|
|
190
|
+
errno = 0;
|
|
191
|
+
if(fcntl(fd, F_GETFD) < 0)
|
|
192
|
+
continue;
|
|
193
|
+
|
|
194
|
+
errno = 0;
|
|
195
|
+
close_func(&data->lowfd, fd);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return Qnil;
|
|
200
|
+
}
|
|
201
|
+
|
|
172
202
|
/*
|
|
173
203
|
* call-seq:
|
|
174
204
|
* IO.fdwalk(lowfd){ |fh| ... }
|
|
@@ -179,12 +209,19 @@ static int close_func(void* lowfd, int fd){
|
|
|
179
209
|
*/
|
|
180
210
|
static VALUE io_fdwalk(int argc, VALUE* argv, VALUE klass){
|
|
181
211
|
VALUE v_low_fd, v_block;
|
|
182
|
-
|
|
212
|
+
struct fdwalk_data data;
|
|
183
213
|
|
|
184
214
|
rb_scan_args(argc, argv, "1&", &v_low_fd, &v_block);
|
|
185
|
-
lowfd = NUM2INT(v_low_fd);
|
|
215
|
+
data.lowfd = NUM2INT(v_low_fd);
|
|
186
216
|
|
|
187
|
-
|
|
217
|
+
if(data.lowfd < 0)
|
|
218
|
+
rb_raise(rb_eArgError, "lowfd must be non-negative");
|
|
219
|
+
|
|
220
|
+
#ifdef PROC_SELF_FD_DIR
|
|
221
|
+
data.dir = opendir(PROC_SELF_FD_DIR);
|
|
222
|
+
#endif
|
|
223
|
+
|
|
224
|
+
rb_ensure(fdwalk_body, (VALUE)&data, fdwalk_ensure, (VALUE)&data);
|
|
188
225
|
|
|
189
226
|
return klass;
|
|
190
227
|
}
|
|
@@ -284,29 +321,51 @@ static VALUE io_set_directio(VALUE self, VALUE v_advice){
|
|
|
284
321
|
}
|
|
285
322
|
#endif
|
|
286
323
|
|
|
287
|
-
/*
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
324
|
+
/* Structure to track iovec allocation */
|
|
325
|
+
struct iovec_buffer {
|
|
326
|
+
struct iovec *iov;
|
|
327
|
+
int iovcnt;
|
|
328
|
+
ssize_t expected;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
/* Convert Ruby array to iovec using heap allocation */
|
|
332
|
+
static void ary2iovec(struct iovec_buffer *buf, VALUE ary) {
|
|
333
|
+
VALUE *cur;
|
|
334
|
+
struct iovec *tmp;
|
|
335
|
+
long n;
|
|
336
|
+
|
|
337
|
+
if (TYPE(ary) != T_ARRAY)
|
|
338
|
+
rb_raise(rb_eArgError, "must be an array of strings");
|
|
339
|
+
|
|
340
|
+
cur = RARRAY_PTR(ary);
|
|
341
|
+
n = RARRAY_LEN(ary);
|
|
342
|
+
|
|
343
|
+
if (n > IOV_MAX)
|
|
344
|
+
rb_raise(rb_eArgError, "array is larger than IOV_MAX");
|
|
345
|
+
|
|
346
|
+
buf->iov = tmp = ALLOC_N(struct iovec, n);
|
|
347
|
+
buf->expected = 0;
|
|
348
|
+
buf->iovcnt = (int)n;
|
|
349
|
+
|
|
350
|
+
for (; --n >= 0; tmp++, cur++) {
|
|
351
|
+
if (TYPE(*cur) != T_STRING) {
|
|
352
|
+
xfree(buf->iov);
|
|
353
|
+
buf->iov = NULL;
|
|
354
|
+
rb_raise(rb_eArgError, "must be an array of strings");
|
|
355
|
+
}
|
|
356
|
+
tmp->iov_base = RSTRING_PTR(*cur);
|
|
357
|
+
tmp->iov_len = RSTRING_LEN(*cur);
|
|
358
|
+
buf->expected += tmp->iov_len;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/* Free iovec buffer */
|
|
363
|
+
static void free_iovec_buffer(struct iovec_buffer *buf) {
|
|
364
|
+
if (buf->iov) {
|
|
365
|
+
xfree(buf->iov);
|
|
366
|
+
buf->iov = NULL;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
310
369
|
|
|
311
370
|
#if defined(HAVE_WRITEV)
|
|
312
371
|
struct writev_args {
|
|
@@ -336,13 +395,17 @@ static VALUE s_io_writev(VALUE klass, VALUE fd, VALUE ary) {
|
|
|
336
395
|
ssize_t result = 0;
|
|
337
396
|
ssize_t left;
|
|
338
397
|
struct writev_args args;
|
|
398
|
+
struct iovec_buffer iov_buf;
|
|
339
399
|
|
|
340
400
|
// Allow a fileno or filehandle
|
|
341
401
|
if(rb_respond_to(fd, rb_intern("fileno")))
|
|
342
402
|
fd = rb_funcall(fd, rb_intern("fileno"), 0, 0);
|
|
343
403
|
|
|
344
404
|
args.fd = NUM2INT(fd);
|
|
345
|
-
|
|
405
|
+
ary2iovec(&iov_buf, ary);
|
|
406
|
+
args.iov = iov_buf.iov;
|
|
407
|
+
args.iovcnt = iov_buf.iovcnt;
|
|
408
|
+
left = iov_buf.expected;
|
|
346
409
|
|
|
347
410
|
for(;;) {
|
|
348
411
|
ssize_t w = (ssize_t)rb_thread_call_without_gvl(
|
|
@@ -359,6 +422,7 @@ static VALUE s_io_writev(VALUE klass, VALUE fd, VALUE ary) {
|
|
|
359
422
|
* we'll let the next write (or close) fail instead */
|
|
360
423
|
break;
|
|
361
424
|
}
|
|
425
|
+
free_iovec_buffer(&iov_buf);
|
|
362
426
|
rb_sys_fail("writev");
|
|
363
427
|
}
|
|
364
428
|
}
|
|
@@ -376,13 +440,25 @@ static VALUE s_io_writev(VALUE klass, VALUE fd, VALUE ary) {
|
|
|
376
440
|
left -= w;
|
|
377
441
|
|
|
378
442
|
// Skip over iovecs we've already written completely
|
|
379
|
-
for(i = 0; i < args.iovcnt; i
|
|
443
|
+
for(i = 0; i < args.iovcnt; i++){
|
|
380
444
|
if (w == 0)
|
|
381
445
|
break;
|
|
382
446
|
|
|
447
|
+
// Bounds check before pointer arithmetic
|
|
448
|
+
if(new_iov == NULL){
|
|
449
|
+
free_iovec_buffer(&iov_buf);
|
|
450
|
+
rb_raise(rb_eRuntimeError, "writev: iovec bounds check failed");
|
|
451
|
+
}
|
|
452
|
+
|
|
383
453
|
// Partially written iov, modify and retry with current iovec in front
|
|
384
454
|
if(new_iov->iov_len > (size_t)w){
|
|
385
|
-
|
|
455
|
+
char* base = (char*)new_iov->iov_base;
|
|
456
|
+
|
|
457
|
+
// Validate base pointer before arithmetic
|
|
458
|
+
if(base == NULL){
|
|
459
|
+
free_iovec_buffer(&iov_buf);
|
|
460
|
+
rb_raise(rb_eRuntimeError, "writev: null iov_base");
|
|
461
|
+
}
|
|
386
462
|
|
|
387
463
|
new_iov->iov_len -= w;
|
|
388
464
|
new_iov->iov_base = (void *)(base + w);
|
|
@@ -390,6 +466,13 @@ static VALUE s_io_writev(VALUE klass, VALUE fd, VALUE ary) {
|
|
|
390
466
|
}
|
|
391
467
|
|
|
392
468
|
w -= new_iov->iov_len;
|
|
469
|
+
new_iov++;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Validate we haven't exceeded bounds before modifying args
|
|
473
|
+
if(i > args.iovcnt){
|
|
474
|
+
free_iovec_buffer(&iov_buf);
|
|
475
|
+
rb_raise(rb_eRuntimeError, "writev: exceeded iovec array bounds");
|
|
393
476
|
}
|
|
394
477
|
|
|
395
478
|
// Retry without the already-written iovecs
|
|
@@ -398,6 +481,7 @@ static VALUE s_io_writev(VALUE klass, VALUE fd, VALUE ary) {
|
|
|
398
481
|
}
|
|
399
482
|
}
|
|
400
483
|
|
|
484
|
+
free_iovec_buffer(&iov_buf);
|
|
401
485
|
return LONG2NUM(result);
|
|
402
486
|
}
|
|
403
487
|
#endif
|
|
@@ -418,8 +502,15 @@ static VALUE io_get_ttyname(VALUE self){
|
|
|
418
502
|
|
|
419
503
|
int fd = NUM2INT(rb_funcall(self, rb_intern("fileno"), 0, 0));
|
|
420
504
|
|
|
421
|
-
if(
|
|
422
|
-
|
|
505
|
+
if(fd < 0)
|
|
506
|
+
rb_raise(rb_eArgError, "invalid file descriptor");
|
|
507
|
+
|
|
508
|
+
errno = 0;
|
|
509
|
+
if(isatty(fd)){
|
|
510
|
+
char *name = ttyname(fd);
|
|
511
|
+
if(name != NULL)
|
|
512
|
+
v_return = rb_str_new2(name);
|
|
513
|
+
}
|
|
423
514
|
|
|
424
515
|
return v_return;
|
|
425
516
|
}
|
|
@@ -428,7 +519,7 @@ static VALUE io_get_ttyname(VALUE self){
|
|
|
428
519
|
/* Adds the IO.closefrom, IO.fdwalk class methods, as well as the IO#directio
|
|
429
520
|
* and IO#directio? instance methods (if supported on your platform).
|
|
430
521
|
*/
|
|
431
|
-
void Init_extra(){
|
|
522
|
+
void Init_extra(void){
|
|
432
523
|
rb_define_singleton_method(rb_cIO, "closefrom", io_closefrom, 1);
|
|
433
524
|
|
|
434
525
|
#ifdef HAVE_FDWALK
|
data/io-extra.gemspec
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rbconfig'
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |spec|
|
|
5
|
+
if File::ALT_SEPARATOR
|
|
6
|
+
STDERR.puts 'Not supported on this platform. Exiting.'
|
|
7
|
+
exit(-1)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
spec.name = 'io-extra'
|
|
11
|
+
spec.version = '1.5.0'
|
|
12
|
+
spec.author = 'Daniel J. Berger'
|
|
13
|
+
spec.license = 'Apache-2.0'
|
|
14
|
+
spec.email = 'djberg96@gmail.com'
|
|
15
|
+
spec.homepage = 'https://github.com/djberg96/io-extra'
|
|
16
|
+
spec.summary = 'Adds extra methods to the IO class'
|
|
17
|
+
spec.test_files = Dir['spec/*_spec.rb']
|
|
18
|
+
spec.extensions = ['ext/extconf.rb']
|
|
19
|
+
spec.cert_chain = ['certs/djberg96_pub.pem']
|
|
20
|
+
spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
|
|
21
|
+
|
|
22
|
+
spec.extra_rdoc_files = [
|
|
23
|
+
'CHANGES.md',
|
|
24
|
+
'README.md',
|
|
25
|
+
'MANIFEST.md',
|
|
26
|
+
'ext/io/extra.c'
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
spec.add_development_dependency('rake')
|
|
30
|
+
spec.add_development_dependency('rspec', '~> 3.9')
|
|
31
|
+
spec.add_development_dependency('rubocop')
|
|
32
|
+
spec.add_development_dependency('rubocop-rspec')
|
|
33
|
+
|
|
34
|
+
spec.metadata = {
|
|
35
|
+
'homepage_uri' => 'https://github.com/djberg96/io-extra',
|
|
36
|
+
'bug_tracker_uri' => 'https://github.com/djberg96/io-extra/issues',
|
|
37
|
+
'changelog_uri' => 'https://github.com/djberg96/io-extra/blob/main/CHANGES.md',
|
|
38
|
+
'documentation_uri' => 'https://github.com/djberg96/io-extra/wiki',
|
|
39
|
+
'source_code_uri' => 'https://github.com/djberg96/io-extra',
|
|
40
|
+
'wiki_uri' => 'https://github.com/djberg96/io-extra/wiki',
|
|
41
|
+
'rubygems_mfa_required' => 'true',
|
|
42
|
+
'github_repo' => 'https://github.com/djberg96/io-extra',
|
|
43
|
+
'funding_uri' => 'https://github.com/sponsors/djberg96',
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
spec.description = <<-EOF
|
|
47
|
+
Adds the IO.closefrom, IO.fdwalk, IO.pread, IO.pwrite, and IO.writev
|
|
48
|
+
singleton methods as well as the IO#directio, IO#directio? and IO#ttyname
|
|
49
|
+
instance methods (for those platforms that support them).
|
|
50
|
+
EOF
|
|
51
|
+
end
|
data/lib/io-extra.rb
CHANGED