fiddle 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +13 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/fiddle/closure.c +345 -0
- data/ext/fiddle/closure.h +8 -0
- data/ext/fiddle/conversions.c +141 -0
- data/ext/fiddle/conversions.h +44 -0
- data/ext/fiddle/extconf.rb +183 -0
- data/ext/fiddle/extlibs +2 -0
- data/ext/fiddle/fiddle.c +454 -0
- data/ext/fiddle/fiddle.h +138 -0
- data/ext/fiddle/function.c +315 -0
- data/ext/fiddle/function.h +8 -0
- data/ext/fiddle/handle.c +479 -0
- data/ext/fiddle/pointer.c +721 -0
- data/ext/fiddle/win32/fficonfig.h +29 -0
- data/ext/fiddle/win32/libffi-3.2.1-mswin.patch +191 -0
- data/ext/fiddle/win32/libffi-config.rb +48 -0
- data/ext/fiddle/win32/libffi.mk.tmpl +96 -0
- data/fiddle.gemspec +23 -0
- data/lib/fiddle.rb +56 -0
- data/lib/fiddle/closure.rb +49 -0
- data/lib/fiddle/cparser.rb +194 -0
- data/lib/fiddle/function.rb +18 -0
- data/lib/fiddle/import.rb +318 -0
- data/lib/fiddle/pack.rb +129 -0
- data/lib/fiddle/struct.rb +244 -0
- data/lib/fiddle/types.rb +72 -0
- data/lib/fiddle/value.rb +113 -0
- metadata +136 -0
data/ext/fiddle/handle.c
ADDED
@@ -0,0 +1,479 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <fiddle.h>
|
3
|
+
|
4
|
+
#define SafeStringValueCStr(v) (rb_check_safe_obj(rb_string_value(&v)), StringValueCStr(v))
|
5
|
+
|
6
|
+
VALUE rb_cHandle;
|
7
|
+
|
8
|
+
struct dl_handle {
|
9
|
+
void *ptr;
|
10
|
+
int open;
|
11
|
+
int enable_close;
|
12
|
+
};
|
13
|
+
|
14
|
+
#ifdef _WIN32
|
15
|
+
# ifndef _WIN32_WCE
|
16
|
+
static void *
|
17
|
+
w32_coredll(void)
|
18
|
+
{
|
19
|
+
MEMORY_BASIC_INFORMATION m;
|
20
|
+
memset(&m, 0, sizeof(m));
|
21
|
+
if( !VirtualQuery(_errno, &m, sizeof(m)) ) return NULL;
|
22
|
+
return m.AllocationBase;
|
23
|
+
}
|
24
|
+
# endif
|
25
|
+
|
26
|
+
static int
|
27
|
+
w32_dlclose(void *ptr)
|
28
|
+
{
|
29
|
+
# ifndef _WIN32_WCE
|
30
|
+
if( ptr == w32_coredll() ) return 0;
|
31
|
+
# endif
|
32
|
+
if( FreeLibrary((HMODULE)ptr) ) return 0;
|
33
|
+
return errno = rb_w32_map_errno(GetLastError());
|
34
|
+
}
|
35
|
+
#define dlclose(ptr) w32_dlclose(ptr)
|
36
|
+
#endif
|
37
|
+
|
38
|
+
static void
|
39
|
+
fiddle_handle_free(void *ptr)
|
40
|
+
{
|
41
|
+
struct dl_handle *fiddle_handle = ptr;
|
42
|
+
if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
|
43
|
+
dlclose(fiddle_handle->ptr);
|
44
|
+
}
|
45
|
+
xfree(ptr);
|
46
|
+
}
|
47
|
+
|
48
|
+
static size_t
|
49
|
+
fiddle_handle_memsize(const void *ptr)
|
50
|
+
{
|
51
|
+
return sizeof(struct dl_handle);
|
52
|
+
}
|
53
|
+
|
54
|
+
static const rb_data_type_t fiddle_handle_data_type = {
|
55
|
+
"fiddle/handle",
|
56
|
+
{0, fiddle_handle_free, fiddle_handle_memsize,},
|
57
|
+
};
|
58
|
+
|
59
|
+
/*
|
60
|
+
* call-seq: close
|
61
|
+
*
|
62
|
+
* Close this handle.
|
63
|
+
*
|
64
|
+
* Calling close more than once will raise a Fiddle::DLError exception.
|
65
|
+
*/
|
66
|
+
static VALUE
|
67
|
+
rb_fiddle_handle_close(VALUE self)
|
68
|
+
{
|
69
|
+
struct dl_handle *fiddle_handle;
|
70
|
+
|
71
|
+
TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
|
72
|
+
if(fiddle_handle->open) {
|
73
|
+
int ret = dlclose(fiddle_handle->ptr);
|
74
|
+
fiddle_handle->open = 0;
|
75
|
+
|
76
|
+
/* Check dlclose for successful return value */
|
77
|
+
if(ret) {
|
78
|
+
#if defined(HAVE_DLERROR)
|
79
|
+
rb_raise(rb_eFiddleError, "%s", dlerror());
|
80
|
+
#else
|
81
|
+
rb_raise(rb_eFiddleError, "could not close handle");
|
82
|
+
#endif
|
83
|
+
}
|
84
|
+
return INT2NUM(ret);
|
85
|
+
}
|
86
|
+
rb_raise(rb_eFiddleError, "dlclose() called too many times");
|
87
|
+
|
88
|
+
UNREACHABLE;
|
89
|
+
}
|
90
|
+
|
91
|
+
static VALUE
|
92
|
+
rb_fiddle_handle_s_allocate(VALUE klass)
|
93
|
+
{
|
94
|
+
VALUE obj;
|
95
|
+
struct dl_handle *fiddle_handle;
|
96
|
+
|
97
|
+
obj = TypedData_Make_Struct(rb_cHandle, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
|
98
|
+
fiddle_handle->ptr = 0;
|
99
|
+
fiddle_handle->open = 0;
|
100
|
+
fiddle_handle->enable_close = 0;
|
101
|
+
|
102
|
+
return obj;
|
103
|
+
}
|
104
|
+
|
105
|
+
static VALUE
|
106
|
+
predefined_fiddle_handle(void *handle)
|
107
|
+
{
|
108
|
+
VALUE obj = rb_fiddle_handle_s_allocate(rb_cHandle);
|
109
|
+
struct dl_handle *fiddle_handle = DATA_PTR(obj);
|
110
|
+
|
111
|
+
fiddle_handle->ptr = handle;
|
112
|
+
fiddle_handle->open = 1;
|
113
|
+
OBJ_FREEZE(obj);
|
114
|
+
return obj;
|
115
|
+
}
|
116
|
+
|
117
|
+
/*
|
118
|
+
* call-seq:
|
119
|
+
* new(library = nil, flags = Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
|
120
|
+
*
|
121
|
+
* Create a new handler that opens +library+ with +flags+.
|
122
|
+
*
|
123
|
+
* If no +library+ is specified or +nil+ is given, DEFAULT is used, which is
|
124
|
+
* the equivalent to RTLD_DEFAULT. See <code>man 3 dlopen</code> for more.
|
125
|
+
*
|
126
|
+
* lib = Fiddle::Handle.new
|
127
|
+
*
|
128
|
+
* The default is dependent on OS, and provide a handle for all libraries
|
129
|
+
* already loaded. For example, in most cases you can use this to access +libc+
|
130
|
+
* functions, or ruby functions like +rb_str_new+.
|
131
|
+
*/
|
132
|
+
static VALUE
|
133
|
+
rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self)
|
134
|
+
{
|
135
|
+
void *ptr;
|
136
|
+
struct dl_handle *fiddle_handle;
|
137
|
+
VALUE lib, flag;
|
138
|
+
char *clib;
|
139
|
+
int cflag;
|
140
|
+
const char *err;
|
141
|
+
|
142
|
+
switch( rb_scan_args(argc, argv, "02", &lib, &flag) ){
|
143
|
+
case 0:
|
144
|
+
clib = NULL;
|
145
|
+
cflag = RTLD_LAZY | RTLD_GLOBAL;
|
146
|
+
break;
|
147
|
+
case 1:
|
148
|
+
clib = NIL_P(lib) ? NULL : SafeStringValueCStr(lib);
|
149
|
+
cflag = RTLD_LAZY | RTLD_GLOBAL;
|
150
|
+
break;
|
151
|
+
case 2:
|
152
|
+
clib = NIL_P(lib) ? NULL : SafeStringValueCStr(lib);
|
153
|
+
cflag = NUM2INT(flag);
|
154
|
+
break;
|
155
|
+
default:
|
156
|
+
rb_bug("rb_fiddle_handle_new");
|
157
|
+
}
|
158
|
+
|
159
|
+
#if defined(_WIN32)
|
160
|
+
if( !clib ){
|
161
|
+
HANDLE rb_libruby_handle(void);
|
162
|
+
ptr = rb_libruby_handle();
|
163
|
+
}
|
164
|
+
else if( STRCASECMP(clib, "libc") == 0
|
165
|
+
# ifdef RUBY_COREDLL
|
166
|
+
|| STRCASECMP(clib, RUBY_COREDLL) == 0
|
167
|
+
|| STRCASECMP(clib, RUBY_COREDLL".dll") == 0
|
168
|
+
# endif
|
169
|
+
){
|
170
|
+
# ifdef _WIN32_WCE
|
171
|
+
ptr = dlopen("coredll.dll", cflag);
|
172
|
+
# else
|
173
|
+
(void)cflag;
|
174
|
+
ptr = w32_coredll();
|
175
|
+
# endif
|
176
|
+
}
|
177
|
+
else
|
178
|
+
#endif
|
179
|
+
ptr = dlopen(clib, cflag);
|
180
|
+
#if defined(HAVE_DLERROR)
|
181
|
+
if( !ptr && (err = dlerror()) ){
|
182
|
+
rb_raise(rb_eFiddleError, "%s", err);
|
183
|
+
}
|
184
|
+
#else
|
185
|
+
if( !ptr ){
|
186
|
+
err = dlerror();
|
187
|
+
rb_raise(rb_eFiddleError, "%s", err);
|
188
|
+
}
|
189
|
+
#endif
|
190
|
+
TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
|
191
|
+
if( fiddle_handle->ptr && fiddle_handle->open && fiddle_handle->enable_close ){
|
192
|
+
dlclose(fiddle_handle->ptr);
|
193
|
+
}
|
194
|
+
fiddle_handle->ptr = ptr;
|
195
|
+
fiddle_handle->open = 1;
|
196
|
+
fiddle_handle->enable_close = 0;
|
197
|
+
|
198
|
+
if( rb_block_given_p() ){
|
199
|
+
rb_ensure(rb_yield, self, rb_fiddle_handle_close, self);
|
200
|
+
}
|
201
|
+
|
202
|
+
return Qnil;
|
203
|
+
}
|
204
|
+
|
205
|
+
/*
|
206
|
+
* call-seq: enable_close
|
207
|
+
*
|
208
|
+
* Enable a call to dlclose() when this handle is garbage collected.
|
209
|
+
*/
|
210
|
+
static VALUE
|
211
|
+
rb_fiddle_handle_enable_close(VALUE self)
|
212
|
+
{
|
213
|
+
struct dl_handle *fiddle_handle;
|
214
|
+
|
215
|
+
TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
|
216
|
+
fiddle_handle->enable_close = 1;
|
217
|
+
return Qnil;
|
218
|
+
}
|
219
|
+
|
220
|
+
/*
|
221
|
+
* call-seq: disable_close
|
222
|
+
*
|
223
|
+
* Disable a call to dlclose() when this handle is garbage collected.
|
224
|
+
*/
|
225
|
+
static VALUE
|
226
|
+
rb_fiddle_handle_disable_close(VALUE self)
|
227
|
+
{
|
228
|
+
struct dl_handle *fiddle_handle;
|
229
|
+
|
230
|
+
TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
|
231
|
+
fiddle_handle->enable_close = 0;
|
232
|
+
return Qnil;
|
233
|
+
}
|
234
|
+
|
235
|
+
/*
|
236
|
+
* call-seq: close_enabled?
|
237
|
+
*
|
238
|
+
* Returns +true+ if dlclose() will be called when this handle is garbage collected.
|
239
|
+
*
|
240
|
+
* See man(3) dlclose() for more info.
|
241
|
+
*/
|
242
|
+
static VALUE
|
243
|
+
rb_fiddle_handle_close_enabled_p(VALUE self)
|
244
|
+
{
|
245
|
+
struct dl_handle *fiddle_handle;
|
246
|
+
|
247
|
+
TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
|
248
|
+
|
249
|
+
if(fiddle_handle->enable_close) return Qtrue;
|
250
|
+
return Qfalse;
|
251
|
+
}
|
252
|
+
|
253
|
+
/*
|
254
|
+
* call-seq: to_i
|
255
|
+
*
|
256
|
+
* Returns the memory address for this handle.
|
257
|
+
*/
|
258
|
+
static VALUE
|
259
|
+
rb_fiddle_handle_to_i(VALUE self)
|
260
|
+
{
|
261
|
+
struct dl_handle *fiddle_handle;
|
262
|
+
|
263
|
+
TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
|
264
|
+
return PTR2NUM(fiddle_handle);
|
265
|
+
}
|
266
|
+
|
267
|
+
static VALUE fiddle_handle_sym(void *handle, VALUE symbol);
|
268
|
+
|
269
|
+
/*
|
270
|
+
* Document-method: sym
|
271
|
+
*
|
272
|
+
* call-seq: sym(name)
|
273
|
+
*
|
274
|
+
* Get the address as an Integer for the function named +name+.
|
275
|
+
*/
|
276
|
+
static VALUE
|
277
|
+
rb_fiddle_handle_sym(VALUE self, VALUE sym)
|
278
|
+
{
|
279
|
+
struct dl_handle *fiddle_handle;
|
280
|
+
|
281
|
+
TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
|
282
|
+
if( ! fiddle_handle->open ){
|
283
|
+
rb_raise(rb_eFiddleError, "closed handle");
|
284
|
+
}
|
285
|
+
|
286
|
+
return fiddle_handle_sym(fiddle_handle->ptr, sym);
|
287
|
+
}
|
288
|
+
|
289
|
+
#ifndef RTLD_NEXT
|
290
|
+
#define RTLD_NEXT NULL
|
291
|
+
#endif
|
292
|
+
#ifndef RTLD_DEFAULT
|
293
|
+
#define RTLD_DEFAULT NULL
|
294
|
+
#endif
|
295
|
+
|
296
|
+
/*
|
297
|
+
* Document-method: sym
|
298
|
+
*
|
299
|
+
* call-seq: sym(name)
|
300
|
+
*
|
301
|
+
* Get the address as an Integer for the function named +name+. The function
|
302
|
+
* is searched via dlsym on RTLD_NEXT.
|
303
|
+
*
|
304
|
+
* See man(3) dlsym() for more info.
|
305
|
+
*/
|
306
|
+
static VALUE
|
307
|
+
rb_fiddle_handle_s_sym(VALUE self, VALUE sym)
|
308
|
+
{
|
309
|
+
return fiddle_handle_sym(RTLD_NEXT, sym);
|
310
|
+
}
|
311
|
+
|
312
|
+
static VALUE
|
313
|
+
fiddle_handle_sym(void *handle, VALUE symbol)
|
314
|
+
{
|
315
|
+
#if defined(HAVE_DLERROR)
|
316
|
+
const char *err;
|
317
|
+
# define CHECK_DLERROR if ((err = dlerror()) != 0) { func = 0; }
|
318
|
+
#else
|
319
|
+
# define CHECK_DLERROR
|
320
|
+
#endif
|
321
|
+
void (*func)();
|
322
|
+
const char *name = SafeStringValueCStr(symbol);
|
323
|
+
|
324
|
+
#ifdef HAVE_DLERROR
|
325
|
+
dlerror();
|
326
|
+
#endif
|
327
|
+
func = (void (*)())(VALUE)dlsym(handle, name);
|
328
|
+
CHECK_DLERROR;
|
329
|
+
#if defined(FUNC_STDCALL)
|
330
|
+
if( !func ){
|
331
|
+
int i;
|
332
|
+
int len = (int)strlen(name);
|
333
|
+
char *name_n;
|
334
|
+
#if defined(__CYGWIN__) || defined(_WIN32) || defined(__MINGW32__)
|
335
|
+
{
|
336
|
+
char *name_a = (char*)xmalloc(len+2);
|
337
|
+
strcpy(name_a, name);
|
338
|
+
name_n = name_a;
|
339
|
+
name_a[len] = 'A';
|
340
|
+
name_a[len+1] = '\0';
|
341
|
+
func = dlsym(handle, name_a);
|
342
|
+
CHECK_DLERROR;
|
343
|
+
if( func ) goto found;
|
344
|
+
name_n = xrealloc(name_a, len+6);
|
345
|
+
}
|
346
|
+
#else
|
347
|
+
name_n = (char*)xmalloc(len+6);
|
348
|
+
#endif
|
349
|
+
memcpy(name_n, name, len);
|
350
|
+
name_n[len++] = '@';
|
351
|
+
for( i = 0; i < 256; i += 4 ){
|
352
|
+
sprintf(name_n + len, "%d", i);
|
353
|
+
func = dlsym(handle, name_n);
|
354
|
+
CHECK_DLERROR;
|
355
|
+
if( func ) break;
|
356
|
+
}
|
357
|
+
if( func ) goto found;
|
358
|
+
name_n[len-1] = 'A';
|
359
|
+
name_n[len++] = '@';
|
360
|
+
for( i = 0; i < 256; i += 4 ){
|
361
|
+
sprintf(name_n + len, "%d", i);
|
362
|
+
func = dlsym(handle, name_n);
|
363
|
+
CHECK_DLERROR;
|
364
|
+
if( func ) break;
|
365
|
+
}
|
366
|
+
found:
|
367
|
+
xfree(name_n);
|
368
|
+
}
|
369
|
+
#endif
|
370
|
+
if( !func ){
|
371
|
+
rb_raise(rb_eFiddleError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
|
372
|
+
}
|
373
|
+
|
374
|
+
return PTR2NUM(func);
|
375
|
+
}
|
376
|
+
|
377
|
+
void
|
378
|
+
Init_fiddle_handle(void)
|
379
|
+
{
|
380
|
+
/*
|
381
|
+
* Document-class: Fiddle::Handle
|
382
|
+
*
|
383
|
+
* The Fiddle::Handle is the manner to access the dynamic library
|
384
|
+
*
|
385
|
+
* == Example
|
386
|
+
*
|
387
|
+
* === Setup
|
388
|
+
*
|
389
|
+
* libc_so = "/lib64/libc.so.6"
|
390
|
+
* => "/lib64/libc.so.6"
|
391
|
+
* @handle = Fiddle::Handle.new(libc_so)
|
392
|
+
* => #<Fiddle::Handle:0x00000000d69ef8>
|
393
|
+
*
|
394
|
+
* === Setup, with flags
|
395
|
+
*
|
396
|
+
* libc_so = "/lib64/libc.so.6"
|
397
|
+
* => "/lib64/libc.so.6"
|
398
|
+
* @handle = Fiddle::Handle.new(libc_so, Fiddle::RTLD_LAZY | Fiddle::RTLD_GLOBAL)
|
399
|
+
* => #<Fiddle::Handle:0x00000000d69ef8>
|
400
|
+
*
|
401
|
+
* See RTLD_LAZY and RTLD_GLOBAL
|
402
|
+
*
|
403
|
+
* === Addresses to symbols
|
404
|
+
*
|
405
|
+
* strcpy_addr = @handle['strcpy']
|
406
|
+
* => 140062278451968
|
407
|
+
*
|
408
|
+
* or
|
409
|
+
*
|
410
|
+
* strcpy_addr = @handle.sym('strcpy')
|
411
|
+
* => 140062278451968
|
412
|
+
*
|
413
|
+
*/
|
414
|
+
rb_cHandle = rb_define_class_under(mFiddle, "Handle", rb_cObject);
|
415
|
+
rb_define_alloc_func(rb_cHandle, rb_fiddle_handle_s_allocate);
|
416
|
+
rb_define_singleton_method(rb_cHandle, "sym", rb_fiddle_handle_s_sym, 1);
|
417
|
+
rb_define_singleton_method(rb_cHandle, "[]", rb_fiddle_handle_s_sym, 1);
|
418
|
+
|
419
|
+
/* Document-const: NEXT
|
420
|
+
*
|
421
|
+
* A predefined pseudo-handle of RTLD_NEXT
|
422
|
+
*
|
423
|
+
* Which will find the next occurrence of a function in the search order
|
424
|
+
* after the current library.
|
425
|
+
*/
|
426
|
+
rb_define_const(rb_cHandle, "NEXT", predefined_fiddle_handle(RTLD_NEXT));
|
427
|
+
|
428
|
+
/* Document-const: DEFAULT
|
429
|
+
*
|
430
|
+
* A predefined pseudo-handle of RTLD_DEFAULT
|
431
|
+
*
|
432
|
+
* Which will find the first occurrence of the desired symbol using the
|
433
|
+
* default library search order
|
434
|
+
*/
|
435
|
+
rb_define_const(rb_cHandle, "DEFAULT", predefined_fiddle_handle(RTLD_DEFAULT));
|
436
|
+
|
437
|
+
/* Document-const: RTLD_GLOBAL
|
438
|
+
*
|
439
|
+
* rtld Fiddle::Handle flag.
|
440
|
+
*
|
441
|
+
* The symbols defined by this library will be made available for symbol
|
442
|
+
* resolution of subsequently loaded libraries.
|
443
|
+
*/
|
444
|
+
rb_define_const(rb_cHandle, "RTLD_GLOBAL", INT2NUM(RTLD_GLOBAL));
|
445
|
+
|
446
|
+
/* Document-const: RTLD_LAZY
|
447
|
+
*
|
448
|
+
* rtld Fiddle::Handle flag.
|
449
|
+
*
|
450
|
+
* Perform lazy binding. Only resolve symbols as the code that references
|
451
|
+
* them is executed. If the symbol is never referenced, then it is never
|
452
|
+
* resolved. (Lazy binding is only performed for function references;
|
453
|
+
* references to variables are always immediately bound when the library
|
454
|
+
* is loaded.)
|
455
|
+
*/
|
456
|
+
rb_define_const(rb_cHandle, "RTLD_LAZY", INT2NUM(RTLD_LAZY));
|
457
|
+
|
458
|
+
/* Document-const: RTLD_NOW
|
459
|
+
*
|
460
|
+
* rtld Fiddle::Handle flag.
|
461
|
+
*
|
462
|
+
* If this value is specified or the environment variable LD_BIND_NOW is
|
463
|
+
* set to a nonempty string, all undefined symbols in the library are
|
464
|
+
* resolved before Fiddle.dlopen returns. If this cannot be done an error
|
465
|
+
* is returned.
|
466
|
+
*/
|
467
|
+
rb_define_const(rb_cHandle, "RTLD_NOW", INT2NUM(RTLD_NOW));
|
468
|
+
|
469
|
+
rb_define_method(rb_cHandle, "initialize", rb_fiddle_handle_initialize, -1);
|
470
|
+
rb_define_method(rb_cHandle, "to_i", rb_fiddle_handle_to_i, 0);
|
471
|
+
rb_define_method(rb_cHandle, "close", rb_fiddle_handle_close, 0);
|
472
|
+
rb_define_method(rb_cHandle, "sym", rb_fiddle_handle_sym, 1);
|
473
|
+
rb_define_method(rb_cHandle, "[]", rb_fiddle_handle_sym, 1);
|
474
|
+
rb_define_method(rb_cHandle, "disable_close", rb_fiddle_handle_disable_close, 0);
|
475
|
+
rb_define_method(rb_cHandle, "enable_close", rb_fiddle_handle_enable_close, 0);
|
476
|
+
rb_define_method(rb_cHandle, "close_enabled?", rb_fiddle_handle_close_enabled_p, 0);
|
477
|
+
}
|
478
|
+
|
479
|
+
/* vim: set noet sws=4 sw=4: */
|