rb-wartslib 0.9.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,52 @@
1
+ /*
2
+ ** Ruby bindings to the functions in libscamperfile.a that are needed to
3
+ ** sort scamper trace files (a la sc_sorter).
4
+ **
5
+ ** --------------------------------------------------------------------------
6
+ ** Copyright (C) 2007 The Regents of the University of California.
7
+ **
8
+ ** This program is free software; you can redistribute it and/or modify
9
+ ** it under the terms of the GNU General Public License as published by
10
+ ** the Free Software Foundation; either version 2 of the License, or
11
+ ** (at your option) any later version.
12
+ **
13
+ ** This program is distributed in the hope that it will be useful,
14
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ ** GNU General Public License for more details.
17
+ **
18
+ ** You should have received a copy of the GNU General Public License
19
+ ** along with this program; if not, write to the Free Software
20
+ ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
+ **
22
+ ** $Id: scext.c,v 1.18 2007/11/29 01:51:54 youngh Exp $
23
+ **/
24
+
25
+ #include "ruby.h"
26
+
27
+ #if defined(__HAVE_STDINT_H__)
28
+ #include <stdint.h>
29
+ #endif
30
+
31
+ #include "scamper.h"
32
+
33
+ #include "scaddr.h"
34
+ #include "sclist.h"
35
+ #include "sctrace.h"
36
+ #include "scfile.h"
37
+
38
+ VALUE mWarts;
39
+
40
+ void Init_wartslibext(void)
41
+ {
42
+ mWarts = rb_define_module("Warts");
43
+
44
+ rb_define_const(mWarts, "WARTS_LIB_VERSION", rb_str_new2("0.9.10"));
45
+ rb_define_const(mWarts, "SCAMPER_VERSION", rb_str_new2(SCAMPER_VERSION));
46
+
47
+ Init_scaddr();
48
+ Init_sclist();
49
+ Init_sccycle();
50
+ Init_sctrace();
51
+ Init_scfile();
52
+ }
@@ -0,0 +1,688 @@
1
+ /*
2
+ ** Ruby bindings to scamper file operations in scamper_file.c.
3
+ **
4
+ ** --------------------------------------------------------------------------
5
+ ** Copyright (C) 2007 The Regents of the University of California.
6
+ **
7
+ ** This program is free software; you can redistribute it and/or modify
8
+ ** it under the terms of the GNU General Public License as published by
9
+ ** the Free Software Foundation; either version 2 of the License, or
10
+ ** (at your option) any later version.
11
+ **
12
+ ** This program is distributed in the hope that it will be useful,
13
+ ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ ** GNU General Public License for more details.
16
+ **
17
+ ** You should have received a copy of the GNU General Public License
18
+ ** along with this program; if not, write to the Free Software
19
+ ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
+ **
21
+ ** $Id: scfile.c,v 1.30 2007/11/29 01:42:07 youngh Exp $
22
+ **/
23
+
24
+ #include <unistd.h>
25
+ #include <string.h>
26
+ #include <strings.h>
27
+
28
+ #include "ruby.h"
29
+
30
+ #include "scfile.h"
31
+ #include "scaddr.h"
32
+
33
+ #if defined(__HAVE_STDINT_H__)
34
+ #include <stdint.h>
35
+ #endif
36
+
37
+ #include "scamper_list.h"
38
+ #include "scamper_addr.h"
39
+ #include "scamper_trace.h"
40
+ #include "scamper_file.h"
41
+
42
+ extern VALUE mWarts;
43
+ extern VALUE cTrace;
44
+ extern VALUE cCycle;
45
+
46
+ static VALUE cFile;
47
+ static ID iv_readable, iv_filters, iv_filters_changed;
48
+ static ID meth_write_to;
49
+ static ID meth_kind_of;
50
+ static ID meth_fileno;
51
+
52
+ typedef struct {
53
+ scamper_file_t *file;
54
+ scamper_file_filter_t *filters;
55
+ } file_data_t;
56
+
57
+ typedef struct {
58
+ const char* name;
59
+ int value;
60
+ } sctype_info_t;
61
+
62
+ sctype_info_t all_sctypes[] = {
63
+ { "LIST", SCAMPER_FILE_OBJ_LIST },
64
+ { "CYCLE_START", SCAMPER_FILE_OBJ_CYCLE_START },
65
+ { "CYCLE_DEF", SCAMPER_FILE_OBJ_CYCLE_DEF },
66
+ { "CYCLE_STOP", SCAMPER_FILE_OBJ_CYCLE_STOP },
67
+ { "ADDR", SCAMPER_FILE_OBJ_ADDR },
68
+ { "TRACE", SCAMPER_FILE_OBJ_TRACE },
69
+ { "PING", SCAMPER_FILE_OBJ_PING }
70
+ };
71
+
72
+ size_t num_sctypes = sizeof(all_sctypes) / sizeof(sctype_info_t);
73
+
74
+ #define MIN_SCTYPE_VALUE SCAMPER_FILE_OBJ_LIST
75
+ #define MAX_SCTYPE_VALUE SCAMPER_FILE_OBJ_PING
76
+
77
+ #define CHECK_WRITABLE(self) \
78
+ if (RTEST(rb_ivar_get(self, iv_readable))) { \
79
+ rb_fatal("attempt to write to a file opened for reading"); \
80
+ }
81
+
82
+ #define CHECK_READABLE(self) \
83
+ if (!RTEST(rb_ivar_get(self, iv_readable))) { \
84
+ rb_fatal("attempt to read from a file opened for writing"); \
85
+ }
86
+
87
+
88
+ /***************************************************************************/
89
+
90
+ static void sf_free(void *arg)
91
+ {
92
+ file_data_t *data = (file_data_t *)arg;
93
+ if (data) {
94
+ if (data->file) {
95
+ scamper_file_close(data->file);
96
+ data->file = NULL;
97
+ }
98
+ if (data->filters) {
99
+ scamper_file_filter_free(data->filters);
100
+ data->filters = NULL;
101
+ }
102
+ free(data);
103
+ }
104
+ }
105
+
106
+ static VALUE scfile_alloc(VALUE klass)
107
+ {
108
+ return Data_Wrap_Struct(klass, 0, (RUBY_DATA_FUNC)sf_free, 0);
109
+ }
110
+
111
+
112
+ /*
113
+ ** Opens either a path or a fd, depending on the type of the first argument.
114
+ ** A file descriptor can be given as either a FIXNUM or as an instance of
115
+ ** IO.
116
+ **
117
+ ** NOTE: It is the responsibility of the caller to (eventually) close any
118
+ ** passed in file descriptor. See comments below for the reason.
119
+ */
120
+ static VALUE scfile_init(int argc, VALUE *argv, VALUE self)
121
+ {
122
+ VALUE path_or_fd, mode, type;
123
+ VALUE path = Qnil;
124
+ int fd = -1, fd_dup = -1;
125
+ char mode_char = 'r';
126
+ char *type_str = NULL;
127
+ scamper_file_t *file = NULL;
128
+ file_data_t *data = NULL;
129
+
130
+ rb_scan_args(argc, argv, "12", &path_or_fd, &mode, &type);
131
+ if (NIL_P(path_or_fd)) {
132
+ rb_raise(rb_eArgError, "missing path or fd");
133
+ }
134
+
135
+ if (TYPE(path_or_fd) == T_FIXNUM || TYPE(path_or_fd) == T_BIGNUM) {
136
+ fd = NUM2INT(path_or_fd);
137
+ }
138
+ else if (RTEST(rb_funcall(path_or_fd, meth_kind_of, 1, rb_cIO))) {
139
+ VALUE fileno = rb_funcall(path_or_fd, meth_fileno, 0);
140
+ fd = FIX2INT(fileno);
141
+ }
142
+ else {
143
+ path = path_or_fd;
144
+ SafeStringValue(path);
145
+ }
146
+
147
+ if (NIL_P(mode)) {
148
+ mode_char = 'r';
149
+ }
150
+ else {
151
+ /* Since scamper_file_open() doesn't return any distinguishable error for
152
+ an invalid mode, we check for validity here. */
153
+ const char *mode_str;
154
+
155
+ StringValue(mode);
156
+ mode_str = RSTRING(mode)->ptr;
157
+ if (strlen(mode_str) != 1 || !index("rwa", mode_str[0])) {
158
+ rb_raise(rb_eArgError, "invalid open mode");
159
+ }
160
+ mode_char = mode_str[0];
161
+ }
162
+
163
+ rb_ivar_set(self, iv_readable, (mode_char == 'r' ? Qtrue : Qfalse));
164
+
165
+ if (NIL_P(type)) {
166
+ if (fd >= 0) {
167
+ type_str = "warts";
168
+ }
169
+ else {
170
+ if (mode_char == 'w' || mode_char == 'a') {
171
+ type_str = "warts";
172
+ }
173
+ /* else let scamper_file_open do file type detection */
174
+ }
175
+ }
176
+ else {
177
+ StringValue(type);
178
+ type_str = RSTRING(type)->ptr;
179
+
180
+ /* Since scamper_file_open() doesn't return any distinguishable error for
181
+ an invalid type, we check for validity here. */
182
+ if (strcmp(type_str, "warts") && strcmp(type_str, "arts")
183
+ && strcmp(type_str, "traceroute")) {
184
+ rb_raise(rb_eArgError, "invalid file type");
185
+ }
186
+ }
187
+
188
+ if (type_str) {
189
+ if ((mode_char == 'w' || mode_char == 'a') && strcmp(type_str, "arts")==0){
190
+ rb_raise(rb_eArgError,"writing or appending to arts files not supported");
191
+ }
192
+
193
+ if (mode_char == 'r' && strcmp(type_str, "traceroute") == 0) {
194
+ rb_raise(rb_eArgError, "reading from traceroute files not supported");
195
+ }
196
+ }
197
+
198
+ data = ALLOC(file_data_t);
199
+ data->file = NULL;
200
+ data->filters = NULL;
201
+ DATA_PTR(self) = data;
202
+
203
+ /*
204
+ ** NOTE: We must be careful when calling scamper_file_openfd(). We
205
+ ** should NOT simply pass in the file descriptor supplied to
206
+ ** scfile_init because the policy of scamper_file_openfd is to
207
+ ** take ownership of the passed-in file descriptor. This means
208
+ ** that, eventually, scamper_file_close closes the file
209
+ ** descriptor. Normally, this "takes-ownership-of-argument"
210
+ ** policy is what you want, but when interfacing the scamper warts
211
+ ** library to Ruby, this policy can cause problems.
212
+ **
213
+ ** An open file in Ruby is more than simply a file
214
+ ** descriptor--there's an allocated object associated with the
215
+ ** file descriptor. Hence, we need to free the object in addition
216
+ ** to closing the file descriptor when closing an open file in
217
+ ** Ruby. So it causes problems when scamper_file_close does half
218
+ ** of this work (by closing the file descriptor) without getting
219
+ ** the Ruby object involved. This results in close() being called
220
+ ** twice on the file descriptor. The first time, the call closes
221
+ ** the actual file; the second time, it closes any unreleated open
222
+ ** file that happens to be re-using the same file descriptor
223
+ ** number (which can be particularly insidious if the extra
224
+ ** close() is delayed till the next garbage collection).
225
+ **
226
+ ** When piping in the output of 'gzip -dc' (in order to
227
+ ** transparently read a compressed warts file), simply closing the
228
+ ** file descriptor isn't sufficient to free up the resource. At
229
+ ** some point after closing the file descriptor, we have to call
230
+ ** waitpid(). Thus, in this case, the takes-ownership policy of
231
+ ** scamper_file_openfd is problematic--the Ruby object needs to
232
+ ** do the resource deallocation itself.
233
+ **
234
+ ** Fortunately, there's an easy solution to the mismatch. We
235
+ ** simply dup the file descriptor and pass the dup to
236
+ ** scamper_file_openfd (and let it take ownership and do what it
237
+ ** will of it).
238
+ */
239
+ if (NIL_P(path)) {
240
+ if ((fd_dup = dup(fd)) < 0) {
241
+ rb_raise(rb_eRuntimeError, "couldn't duplicate file descriptor");
242
+ }
243
+ file = scamper_file_openfd(fd_dup, NULL, mode_char, type_str);
244
+ }
245
+ else {
246
+ file = scamper_file_open(RSTRING(path)->ptr, mode_char, type_str);
247
+ }
248
+
249
+ if (file) {
250
+ data->file = file;
251
+ return self;
252
+ }
253
+ else {
254
+ if (fd_dup >= 0) {
255
+ close(fd_dup);
256
+ }
257
+ return Qnil;
258
+ }
259
+ }
260
+
261
+
262
+ static VALUE scfile_type(VALUE self)
263
+ {
264
+ file_data_t *data = NULL;
265
+
266
+ Data_Get_Struct(self, file_data_t, data);
267
+ if (data->file) {
268
+ char buf [128];
269
+ return rb_str_new2(scamper_file_type_tostr(data->file, buf, 128));
270
+ }
271
+ return Qnil;
272
+ }
273
+
274
+
275
+ static VALUE scfile_path(VALUE self)
276
+ {
277
+ file_data_t *data = NULL;
278
+
279
+ Data_Get_Struct(self, file_data_t, data);
280
+ if (data->file) {
281
+ return rb_str_new2(scamper_file_getfilename(data->file));
282
+ }
283
+ return Qnil;
284
+ }
285
+
286
+
287
+ static VALUE scfile_close(VALUE self)
288
+ {
289
+ file_data_t *data = NULL;
290
+
291
+ Data_Get_Struct(self, file_data_t, data);
292
+
293
+ if (data->file) {
294
+ scamper_file_close(data->file);
295
+ data->file = NULL;
296
+ }
297
+
298
+ return self;
299
+ }
300
+
301
+
302
+ static VALUE scfile_closed(VALUE self)
303
+ {
304
+ file_data_t *data = NULL;
305
+
306
+ Data_Get_Struct(self, file_data_t, data);
307
+
308
+ return (data->file ? Qfalse : Qtrue);
309
+ }
310
+
311
+
312
+ static VALUE scfile_s_open(int argc, VALUE *argv, VALUE klass)
313
+ {
314
+ VALUE obj = Data_Wrap_Struct(klass, 0, sf_free, 0);
315
+
316
+ if (NIL_P(scfile_init(argc, argv, obj))) {
317
+ return Qnil;
318
+ }
319
+
320
+ return (rb_block_given_p() ? rb_ensure(rb_yield, obj, scfile_close, obj)
321
+ : obj);
322
+ }
323
+
324
+
325
+ static void update_filters(VALUE self)
326
+ {
327
+ file_data_t *data = NULL;
328
+ VALUE filters;
329
+ int i;
330
+
331
+ if (RTEST(rb_ivar_get(self, iv_filters_changed))) {
332
+ uint16_t filter_values[MAX_SCTYPE_VALUE];
333
+ uint16_t num_values = 0;
334
+
335
+ rb_ivar_set(self, iv_filters_changed, Qnil);
336
+
337
+ Data_Get_Struct(self, file_data_t, data);
338
+ if (data->filters) {
339
+ scamper_file_filter_free(data->filters);
340
+ data->filters = NULL;
341
+ }
342
+
343
+ filters = rb_ivar_get(self, iv_filters);
344
+ if (!NIL_P(filters) && RARRAY(filters)->len > 0) {
345
+ for (i = 0; i < RARRAY(filters)->len; i++) {
346
+ if (RTEST(rb_ary_entry(filters, i))) {
347
+ filter_values[num_values++] = i;
348
+ }
349
+ }
350
+
351
+ if (num_values > 0) {
352
+ data->filters = scamper_file_filter_alloc(filter_values, num_values);
353
+ }
354
+ }
355
+ }
356
+ }
357
+
358
+
359
+ #define RETURN(value) \
360
+ if (rb_block_given_p()) { \
361
+ rb_yield(value); \
362
+ goto loop; \
363
+ } else return (value)
364
+
365
+ static VALUE scfile_read(VALUE self)
366
+ {
367
+ file_data_t *data = NULL;
368
+ uint16_t obj_type = 0;
369
+ void *obj_data = NULL;
370
+ VALUE obj;
371
+ int result;
372
+
373
+ Data_Get_Struct(self, file_data_t, data);
374
+ if (!data->file) {
375
+ return Qnil;
376
+ }
377
+ CHECK_READABLE(self)
378
+
379
+ update_filters(self);
380
+
381
+ loop:
382
+ result = scamper_file_read(data->file, data->filters, &obj_type, &obj_data);
383
+ if (result == -1) {
384
+ rb_raise(rb_eIOError, "couldn't read warts file data");
385
+ }
386
+ if (!obj_data) {
387
+ return Qnil; /* EOF */
388
+ }
389
+
390
+ switch (obj_type) {
391
+ case SCAMPER_FILE_OBJ_LIST:
392
+ RETURN(sclist_create((scamper_list_t *)obj_data));
393
+
394
+ case SCAMPER_FILE_OBJ_CYCLE_START:
395
+ case SCAMPER_FILE_OBJ_CYCLE_DEF:
396
+ case SCAMPER_FILE_OBJ_CYCLE_STOP:
397
+ RETURN(sccycle_create((scamper_cycle_t *)obj_data, obj_type));
398
+
399
+ case SCAMPER_FILE_OBJ_ADDR:
400
+ RETURN(scaddr_create((scamper_addr_t *)obj_data));
401
+
402
+ case SCAMPER_FILE_OBJ_TRACE:
403
+ RETURN(sctrace_create((scamper_trace_t *)obj_data));
404
+
405
+ default:
406
+ rb_notimplement();
407
+ }
408
+ }
409
+
410
+ #undef RETURN
411
+
412
+
413
+ /*
414
+ ** This method initiates multiple dispatch with obj. Obj.write_to(file)
415
+ ** should turn around and invoke a suitable write_xxx method; e.g., a trace
416
+ ** object should call file.write_trace(self), and a ping object should call
417
+ ** file.write_ping(self).
418
+ */
419
+ static VALUE scfile_write(VALUE self, VALUE obj)
420
+ {
421
+ rb_funcall(obj, meth_write_to, 1, self);
422
+ }
423
+
424
+
425
+ /*
426
+ ** There's a bug in scamper_file_warts.c:warts_list_write
427
+ ** (scamper-cvs-20070523e & f):
428
+ **
429
+ ** $ split-traces-by-type trmethod-20071106-list.one24.100000.warts
430
+ ** scamper_file_warts.c:1523: failed assertion `off == len'
431
+ ** Abort trap
432
+ **
433
+ ** This bug is triggered when a list object is missing BOTH
434
+ ** a description and a monitor name. As a workaround, we set the
435
+ ** description and monitor name to empty strings if they aren't
436
+ ** already set.
437
+ */
438
+ static void warts_list_write_bug_workaround(scamper_list_t *list)
439
+ {
440
+ if (!list) return;
441
+ if (!list->descr) list->descr = strdup("");
442
+ if (!list->monitor) list->monitor = strdup("");
443
+ }
444
+
445
+
446
+ static VALUE scfile_write_trace(VALUE self, VALUE obj)
447
+ {
448
+ file_data_t *data = NULL;
449
+ scamper_trace_t *trace = NULL;
450
+ int result;
451
+
452
+ if (TYPE(obj) != T_DATA || !RTEST(rb_obj_is_instance_of(obj, cTrace))) {
453
+ rb_raise(rb_eArgError, "argument must be instance of Warts::Trace");
454
+ }
455
+
456
+ Data_Get_Struct(self, file_data_t, data);
457
+ if (!data->file) {
458
+ return Qnil;
459
+ }
460
+ CHECK_WRITABLE(self)
461
+
462
+ Data_Get_Struct(obj, scamper_trace_t, trace);
463
+ warts_list_write_bug_workaround(trace->list);
464
+ if (trace->cycle) {
465
+ warts_list_write_bug_workaround(trace->cycle->list);
466
+ }
467
+
468
+ result = scamper_file_write_trace(data->file, trace);
469
+ if (result == -1) {
470
+ rb_raise(rb_eIOError, "couldn't write warts file data");
471
+ }
472
+ return self;
473
+ }
474
+
475
+
476
+ /*
477
+ ** NOTE: We could have gone with a single scfile_write_cycle() function
478
+ ** that determines what scamper function to call based on the type
479
+ ** of the cycle object. However, such a design might prove
480
+ ** inconvenient for the user since cycle objects are immutable
481
+ ** in this extension. If there were only a single function, then
482
+ ** users would have to be careful to hang onto the START/STOP
483
+ ** cycle objects when sorting traces into files.
484
+ */
485
+ static VALUE scfile_write_cycle_start(VALUE self, VALUE obj)
486
+ {
487
+ file_data_t *data = NULL;
488
+ scamper_cycle_t *cycle = NULL;
489
+ int result;
490
+
491
+ if (TYPE(obj) != T_DATA || !RTEST(rb_obj_is_instance_of(obj, cCycle))) {
492
+ rb_raise(rb_eArgError, "argument must be instance of Warts::Cycle");
493
+ }
494
+
495
+ Data_Get_Struct(self, file_data_t, data);
496
+ if (!data->file) {
497
+ return Qnil;
498
+ }
499
+ CHECK_WRITABLE(self)
500
+
501
+ Data_Get_Struct(obj, scamper_cycle_t, cycle);
502
+ warts_list_write_bug_workaround(cycle->list);
503
+
504
+ result = scamper_file_write_cycle_start(data->file, cycle);
505
+ if (result == -1) {
506
+ rb_raise(rb_eIOError, "couldn't write warts file data");
507
+ }
508
+ return self;
509
+ }
510
+
511
+
512
+ static VALUE scfile_write_cycle_stop(VALUE self, VALUE obj)
513
+ {
514
+ file_data_t *data = NULL;
515
+ scamper_cycle_t *cycle = NULL;
516
+ int result;
517
+
518
+ if (TYPE(obj) != T_DATA || !RTEST(rb_obj_is_instance_of(obj, cCycle))) {
519
+ rb_raise(rb_eArgError, "argument must be instance of Warts::Cycle");
520
+ }
521
+
522
+ Data_Get_Struct(self, file_data_t, data);
523
+ if (!data->file) {
524
+ return Qnil;
525
+ }
526
+ CHECK_WRITABLE(self)
527
+
528
+ Data_Get_Struct(obj, scamper_cycle_t, cycle);
529
+ warts_list_write_bug_workaround(cycle->list);
530
+
531
+ result = scamper_file_write_cycle_stop(data->file, cycle);
532
+ if (result == -1) {
533
+ rb_raise(rb_eIOError, "couldn't write warts file data");
534
+ }
535
+ return self;
536
+ }
537
+
538
+
539
+ /*-------------------------------------------------------------------------*/
540
+
541
+ static VALUE get_iv_filters(VALUE self)
542
+ {
543
+ VALUE filters = rb_ivar_get(self, iv_filters);
544
+ if (NIL_P(filters)) {
545
+ filters = rb_ary_new();
546
+ rb_ivar_set(self, iv_filters, filters);
547
+ }
548
+ return filters;
549
+ }
550
+
551
+
552
+ /* NOTE: This is a helper function: don't directly invoke as a Ruby method. */
553
+ static VALUE scfile_set_filters(int argc, VALUE *argv, VALUE self, VALUE value)
554
+ {
555
+ VALUE filters, given_filters, filter;
556
+ int i, filter_value;
557
+
558
+ filters = get_iv_filters(self);
559
+
560
+ rb_scan_args(argc, argv, "*", &given_filters);
561
+ for (i = 0; i < RARRAY(given_filters)->len; i++) {
562
+ filter = rb_ary_entry(given_filters, i);
563
+ if (RTEST(filter)) {
564
+ filter_value = NUM2INT(filter);
565
+ if (filter_value < MIN_SCTYPE_VALUE || filter_value > MAX_SCTYPE_VALUE) {
566
+ rb_raise(rb_eArgError, "invalid filter value %d", filter_value);
567
+ }
568
+
569
+ rb_ary_store(filters, filter_value, value);
570
+ }
571
+ }
572
+
573
+ rb_ivar_set(self, iv_filters_changed, Qtrue);
574
+ return self;
575
+ }
576
+
577
+
578
+ static VALUE scfile_add_all_filters(VALUE self)
579
+ {
580
+ VALUE filters;
581
+ int i;
582
+
583
+ filters = get_iv_filters(self);
584
+
585
+ for (i = 0; i < num_sctypes; i++) {
586
+ rb_ary_store(filters, all_sctypes[i].value, Qtrue);
587
+ }
588
+
589
+ rb_ivar_set(self, iv_filters_changed, Qtrue);
590
+ return self;
591
+ }
592
+
593
+
594
+ static VALUE scfile_add_filters(int argc, VALUE *argv, VALUE self)
595
+ {
596
+ return scfile_set_filters(argc, argv, self, Qtrue);
597
+ }
598
+
599
+
600
+ static VALUE scfile_clear_all_filters(VALUE self)
601
+ {
602
+ rb_ivar_set(self, iv_filters, Qnil);
603
+ rb_ivar_set(self, iv_filters_changed, Qtrue);
604
+ return self;
605
+ }
606
+
607
+
608
+ static VALUE scfile_clear_filters(int argc, VALUE *argv, VALUE self)
609
+ {
610
+ return scfile_set_filters(argc, argv, self, Qnil);
611
+ }
612
+
613
+
614
+ static VALUE scfile_get_filters(VALUE self)
615
+ {
616
+ VALUE retval, filters;
617
+ int i;
618
+
619
+ retval = rb_ary_new();
620
+
621
+ filters = rb_ivar_get(self, iv_filters);
622
+ if (!NIL_P(filters)) {
623
+ for (i = 0; i < RARRAY(filters)->len; i++) {
624
+ if (RTEST(rb_ary_entry(filters, i))) {
625
+ rb_ary_push(retval, INT2FIX(i));
626
+ }
627
+ }
628
+ }
629
+ return retval;
630
+ }
631
+
632
+
633
+ /***************************************************************************/
634
+ /***************************************************************************/
635
+
636
+ void Init_scfile(void)
637
+ {
638
+ ID private_ID, dup_ID, clone_ID;
639
+ int i;
640
+
641
+ iv_readable = rb_intern("@readable");
642
+ iv_filters = rb_intern("@filters");
643
+ iv_filters_changed = rb_intern("@filters_changed");
644
+ meth_write_to = rb_intern("write_to");
645
+ meth_kind_of = rb_intern("kind_of?");
646
+ meth_fileno = rb_intern("fileno");
647
+
648
+ cFile = rb_define_class_under(mWarts, "File", rb_cObject);
649
+
650
+ for (i = 0; i < num_sctypes; i++) {
651
+ rb_define_const(cFile, all_sctypes[i].name,
652
+ INT2FIX(all_sctypes[i].value));
653
+ }
654
+
655
+ rb_define_const(cFile, "TRACEROUTE", rb_str_new2("traceroute"));
656
+ rb_define_const(cFile, "ARTS", rb_str_new2("arts"));
657
+ rb_define_const(cFile, "WARTS", rb_str_new2("warts"));
658
+
659
+ rb_define_singleton_method(cFile, "open", scfile_s_open, -1);
660
+
661
+ rb_define_alloc_func(cFile, scfile_alloc);
662
+
663
+ rb_define_method(cFile, "initialize", scfile_init, -1);
664
+ rb_define_method(cFile, "type", scfile_type, 0);
665
+ rb_define_method(cFile, "path", scfile_path, 0);
666
+ rb_define_method(cFile, "close", scfile_close, 0);
667
+ rb_define_method(cFile, "closed?", scfile_closed, 0);
668
+ rb_define_method(cFile, "read", scfile_read, 0);
669
+ rb_define_method(cFile, "write", scfile_write, 1);
670
+ rb_define_method(cFile, "write_trace", scfile_write_trace, 1);
671
+ rb_define_method(cFile, "write_cycle_start", scfile_write_cycle_start, 1);
672
+ rb_define_method(cFile, "write_cycle_stop", scfile_write_cycle_stop, 1);
673
+
674
+ rb_define_alias(cFile, "each", "read");
675
+
676
+ rb_define_method(cFile, "add_all_filters", scfile_add_all_filters, 0);
677
+ rb_define_method(cFile, "add_filters", scfile_add_filters, -1);
678
+ rb_define_method(cFile, "clear_all_filters", scfile_clear_all_filters, 0);
679
+ rb_define_method(cFile, "clear_filters", scfile_clear_filters, -1);
680
+ rb_define_method(cFile, "filters", scfile_get_filters, 0);
681
+
682
+ private_ID = rb_intern("private");
683
+ dup_ID = rb_intern("dup");
684
+ clone_ID = rb_intern("clone");
685
+
686
+ rb_funcall(cFile, private_ID, 1, ID2SYM(dup_ID));
687
+ rb_funcall(cFile, private_ID, 1, ID2SYM(clone_ID));
688
+ }