supplement 1.6

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MWJlMThjNWM2NDk3ZmFiMWQ3Y2ZkYWIwZjg2YzhlYjgxYWZjOTE5Mw==
5
+ data.tar.gz: !binary |-
6
+ MGZhYmI5YjQxMWQ0MjNiN2Y0YzVhZjcxY2VmZTkwZWU0YzQ2ZDg4Nw==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ Y2M2MTcyZGEyMGMxZjFiZTdjNDM5NzM3OWFjNjRiMzQ4Y2I3MmUxZmNjZTQ4
10
+ MTkyNTNlOTk5NTlkNzhlZmExODE3NmEzZTMxYjBkMzU3ZDY3NTNlMjcxMTQ4
11
+ N2ViZWM2YWFjNjMzNTkwNjhhOWRhZDlhNWE1Njg3NmMyMDYxMzA=
12
+ data.tar.gz: !binary |-
13
+ NDhjNzViMTYzNTljZGNkNzQwYTAyMTI2YWIzYzNlODA3ZWRjZmI0MThlOTUx
14
+ OWUyMmRkYjQwYjcyNWZlNWZkYWFlZTFkMzY4MjFlOTcwYmQxNTQzNjY1ZmU3
15
+ NDgwNTljMGU4NzNlYzgxMjk4N2Y4MWZjZTEwMGY1NTM2M2VhZDk=
data/LICENSE ADDED
@@ -0,0 +1,34 @@
1
+ _ _
2
+ ___ _ _ _ __ _ __ | | ___ _ __ ___ ___ _ __ | |_
3
+ / __| | | | '_ \| '_ \| |/ _ \ '_ ` _ \ / _ \ '_ \| __|
4
+ \__ \ |_| | |_) | |_) | | __/ | | | | | __/ | | | |_
5
+ |___/\__,_| .__/| .__/|_|\___|_| |_| |_|\___|_| |_|\__|
6
+ |_| |_|
7
+
8
+ Copyright (c) 2009-2013, Bertram Scharpf <software@bertram-scharpf.de>.
9
+ All rights reserved.
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions are
13
+ met:
14
+
15
+ * Redistributions of source code must retain the above copyright
16
+ notice, this list of conditions and the following disclaimer.
17
+
18
+ * Redistributions in binary form must reproduce the above copyright
19
+ notice, this list of conditions and the following disclaimer in
20
+ the documentation and/or other materials provided with the
21
+ distribution.
22
+
23
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
27
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+
data/README ADDED
@@ -0,0 +1,50 @@
1
+ = supplement 1.6 -- Useful Ruby enhancements
2
+
3
+ Some simple Ruby extensions.
4
+
5
+ == Description
6
+
7
+ These are methods on standard classes that didn't manage to become part of
8
+ the Ruby interpreter. (Although, some are part of Ruby 1.9/1.8.7 but not
9
+ 1.8.6).
10
+
11
+ They are all very small, most of them have less than 15 lines of code.
12
+ So they would not be a weight in the Ruby interpreter but it is very
13
+ difficult to convince a majority that they belong there.
14
+
15
+ The methods won't be useful for everybody but some programmers may like
16
+ to make them part of their programming style.
17
+
18
+ If you like to get taunted, then go to the Ruby mailing list and propose
19
+ one of them to be included into the Ruby standard.
20
+
21
+ == Intention
22
+
23
+ In my own code I use quite often a method <code>String#notempty?</code>
24
+ that does almost the same as <code>Numeric#nonzero?</code>. Every
25
+ attempt proposing it for the Ruby standard implementation has failed;
26
+ the discussion evolves the same every time.
27
+
28
+ Now here it is where I can just point to.
29
+
30
+ == Contents (uncomplete)
31
+
32
+ * String#notempty?
33
+ * String#clear
34
+ * String#head
35
+ * String#tail
36
+ * String#rest
37
+ * String#start_with?
38
+ * String#end_with?
39
+ * Array#notempty?
40
+ * Hash#notempty?
41
+ * Struct.[]
42
+ * Interval timer
43
+ * File system stats
44
+ * File#flockb using a block
45
+
46
+ == Warning
47
+
48
+ The RDoc in this project doesn't work properly. If in doubt, refer to the
49
+ source code.
50
+
data/examples/teatimer ADDED
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # teatimer -- Countdown timer
5
+ #
6
+
7
+ # This is an example application for the itimer library.
8
+ # Example:
9
+ #
10
+ # $ teatimer 3m play bell.wav
11
+ # # Count down 3 minutes and the exec the player.
12
+ #
13
+
14
+ require "supplement/itimer"
15
+
16
+
17
+ class TeaTimer
18
+
19
+ M = 60
20
+ H = 60 * M
21
+
22
+ class <<self
23
+
24
+ def from_str time
25
+ seconds = case time
26
+ when /m\z/ then Integer( $`) * M
27
+ when /h\z/ then Integer( $`) * H
28
+ else divide_colons time
29
+ end
30
+ new seconds
31
+ end
32
+
33
+ def run time
34
+ t = from_str time
35
+ t.run
36
+ end
37
+
38
+ private
39
+
40
+ def divide_colons time
41
+ a = time.split ":", -1
42
+ a.map! { |x| Integer( x) }
43
+ a.reverse!
44
+ r = a.shift
45
+ if a.first then r += M * a.shift end
46
+ if a.first then r += H * a.shift end
47
+ a.empty? or raise "Illegal time spec: #{time}."
48
+ r
49
+ end
50
+
51
+ end
52
+
53
+ def initialize seconds
54
+ @seconds = seconds
55
+ end
56
+
57
+ class Done < Exception ; end
58
+
59
+ def run
60
+ trap "SIGALRM" do
61
+ @seconds -= 1
62
+ print_rest
63
+ raise Done if @seconds.zero?
64
+ end
65
+ Process.setitimer 1
66
+ print_rest
67
+ sleep
68
+ rescue Done
69
+ puts
70
+ ensure
71
+ trap "SIGALRM", "DEFAULT"
72
+ Process.setitimer nil
73
+ end
74
+
75
+ private
76
+
77
+ def print_rest
78
+ $stdout.print " \r%02d:%02d:%02d" %
79
+ [ @seconds / H, @seconds % H / M, @seconds % M]
80
+ $stdout.flush
81
+ end
82
+
83
+ end
84
+
85
+ TeaTimer.run $*.first =~ /\A\d+/ ? $*.shift : "3m"
86
+ exec *$* if $*.any?
87
+
data/lib/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ #
2
+ # Rakefile -- build some libraries
3
+ #
4
+
5
+ require "autorake"
6
+
7
+ c = compiler "-O2", "-fPIC"
8
+ l = linker "-shared"
9
+
10
+ rule ".o" => ".c" do |t|
11
+ c.cc t.name, t.source
12
+ end
13
+
14
+ DLs = {
15
+ "supplement.so" => %w(supplement.o sync.o),
16
+ "supplement/filesys.so" => %w(supplement/filesys.o),
17
+ "supplement/itimer.so" => %w(supplement/itimer.o),
18
+ "supplement/terminal.so" => %w(supplement/terminal.o),
19
+ }
20
+
21
+ DLs.each { |k,v|
22
+ task k => v do |t|
23
+ l.cc t.name, t.prerequisites
24
+ end
25
+ }
26
+
27
+ task :default => DLs.keys
28
+
29
+ task :clean do
30
+ FileList[ "**/*.o", "**/*.so"].each { |f| rm_f f }
31
+ end
32
+
data/lib/mkrf_conf ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # mkrf_conf -- configure Supplement
5
+ #
6
+
7
+ require "autorake/mkconfig"
8
+
9
+ Autorake.configure {
10
+
11
+ extending_ruby
12
+
13
+ if RUBY_VERSION < "1.9.2" then
14
+ enable :array_select_bang
15
+ if RUBY_VERSION < "1.9" then
16
+ enable :string_ord
17
+ enable :string_clear
18
+ if RUBY_VERSION < "1.8.7" then
19
+ enable :array_index_with_block
20
+ enable :kernel_tap
21
+ enable :string_start_with
22
+ enable :thread_exclusive
23
+ end
24
+ have_header "rubysig.h"
25
+ end
26
+ end
27
+
28
+ if RUBY_VERSION < "1.9" then
29
+ have_header "ruby.h"
30
+ have_header "st.h"
31
+ have_header "rubyio.h"
32
+ have_header "re.h"
33
+ else
34
+ have_header "ruby/ruby.h"
35
+ have_header "ruby/st.h"
36
+ have_header "ruby/io.h"
37
+ have_header "ruby/re.h"
38
+ end
39
+
40
+ }
41
+
data/lib/supplement.c ADDED
@@ -0,0 +1,1657 @@
1
+ /*
2
+ * supplement.c -- Simple Ruby Extensions
3
+ */
4
+
5
+ #include "supplement.h"
6
+
7
+ #include "sync.h"
8
+
9
+ #if HAVE_HEADER_ST_H
10
+ #include <st.h>
11
+ #elif HAVE_HEADER_RUBY_ST_H
12
+ #include <ruby/st.h>
13
+ #endif
14
+ #if HAVE_HEADER_RUBYIO_H
15
+ #include <rubyio.h>
16
+ #elif HAVE_HEADER_RUBY_IO_H
17
+ #include <ruby/io.h>
18
+ #endif
19
+ #if HAVE_HEADER_RE_H
20
+ #include <re.h>
21
+ #elif HAVE_HEADER_RUBY_RE_H
22
+ #include <ruby/re.h>
23
+ #endif
24
+
25
+ #ifdef FEATURE_THREAD_EXCLUSIVE
26
+ #ifdef HAVE_HEADER_RUBYSIG_H
27
+ #include <rubysig.h>
28
+ #endif
29
+ #endif
30
+
31
+
32
+ #include <sys/stat.h>
33
+ #include <sys/file.h>
34
+ #include <math.h>
35
+
36
+
37
+
38
+ #ifdef HAVE_HEADER_RUBY_H
39
+ /* Oh what a bug! */
40
+ #define R_MATCH( obj) RMATCH( obj)
41
+ #else
42
+ #endif
43
+
44
+
45
+ struct supplement_flock {
46
+ struct supplement_flock *prev;
47
+ VALUE file;
48
+ int op;
49
+ int last_op;
50
+ };
51
+
52
+ static struct supplement_flock *flocks_root = NULL;
53
+
54
+
55
+ static VALUE supplement_index_blk( VALUE);
56
+ static VALUE supplement_rindex_blk( VALUE);
57
+ #ifdef FEATURE_ARRAY_INDEX_WITH_BLOCK
58
+ static VALUE supplement_index_val( VALUE, VALUE);
59
+ static VALUE supplement_rindex_val( VALUE, VALUE);
60
+ #endif
61
+ #ifdef FEATURE_ARRAY_SELECT_BANG
62
+ static VALUE supplement_reject( VALUE);
63
+ static VALUE supplement_invert_yield( VALUE);
64
+ #endif
65
+ static VALUE supplement_each_line( VALUE);
66
+ static void supplement_init_flock( struct supplement_flock *, VALUE, VALUE);
67
+ static VALUE supplement_do_unflock( VALUE);
68
+ static VALUE supplement_do_unumask( VALUE);
69
+ static VALUE supplement_chdir( VALUE);
70
+ #ifdef FEATURE_THREAD_EXCLUSIVE
71
+ static VALUE bsruby_set_thread_critical( VALUE);
72
+ #endif
73
+
74
+
75
+
76
+ static ID id_delete_at;
77
+ static ID id_cmp;
78
+ static ID id_eqq;
79
+ #ifdef FEATURE_ARRAY_SELECT_BANG
80
+ static ID id_reject_bang;
81
+ #endif
82
+ static ID id_chdir;
83
+ static ID id_path;
84
+ static ID id_mkdir;
85
+ static ID id_index;
86
+
87
+
88
+
89
+ /*
90
+ * Document-class: Object
91
+ */
92
+
93
+ /*
94
+ * call-seq:
95
+ * new_string -> str
96
+ *
97
+ * Returns another string that may be modified without touching the original
98
+ * object. This means +dup+ for a string and +to_s+ for any other object.
99
+ *
100
+ * If a block is given, that may modify a created string (built from a
101
+ * non-string object). For a dup'ed string object the block will not be
102
+ * called.
103
+ */
104
+
105
+ VALUE
106
+ rb_obj_new_string( VALUE obj)
107
+ {
108
+ VALUE r;
109
+
110
+ r = rb_obj_as_string( obj);
111
+ if (rb_block_given_p())
112
+ rb_yield( r);
113
+ return r;
114
+ }
115
+
116
+
117
+ /*
118
+ * call-seq:
119
+ * nil_if val -> nil or self
120
+ *
121
+ * Returns +nil+ when the string matches +val+. +val+ is compared using
122
+ * the <code>===</code> operator, just like in the case statement.
123
+ *
124
+ * "hello".nil_if "NONE" #=> "hello"
125
+ * "NONE".nil_if "NONE" #=> nil
126
+ * "NONE".nil_if /^none$/i #=> nil
127
+ *
128
+ * 20.nil_if 10 #=> 20
129
+ * 10.nil_if 10 #=> nil
130
+ * 1.0.nil_if Float #=> nil
131
+ *
132
+ */
133
+
134
+ VALUE
135
+ rb_obj_nil_if( VALUE obj, VALUE val)
136
+ {
137
+ if (!id_eqq)
138
+ id_eqq = rb_intern( "===");
139
+ return RTEST( rb_funcall( val, id_eqq, 1, obj)) ? Qnil : obj;
140
+ }
141
+
142
+
143
+ /*
144
+ * Document-module: Kernel
145
+ */
146
+
147
+ #ifdef FEATURE_KERNEL_TAP
148
+
149
+ /*
150
+ * call-seq:
151
+ * tap { |x| ... } -> obj
152
+ *
153
+ * Yields <code>x</code> to the block, and then returns <code>x</code>.
154
+ * The primary purpose of this method is to "tap into" a method chain,
155
+ * in order to perform operations on intermediate results within the chain.
156
+ *
157
+ * (1..10) .tap { |x| puts "original: #{x.inspect}" }
158
+ * .to_a .tap { |x| puts "array: #{x.inspect}" }
159
+ * .select { |x| x%2==0 } .tap { |x| puts "evens: #{x.inspect}" }
160
+ * .map { |x| x*x } .tap { |x| puts "squares: #{x.inspect}" }
161
+ *
162
+ */
163
+
164
+ VALUE
165
+ rb_krn_tap( VALUE obj)
166
+ {
167
+ rb_yield( obj);
168
+ return obj;
169
+ }
170
+
171
+ #endif
172
+
173
+ /*
174
+ * call-seq:
175
+ * tap! { |x| ... } -> obj
176
+ *
177
+ * Yields +x+ to the block, and then returns +x+ if and only
178
+ * if +x+ is not +nil+.
179
+ *
180
+ */
181
+
182
+ VALUE
183
+ rb_krn_tap_bang( VALUE obj)
184
+ {
185
+ if (!NIL_P( obj))
186
+ rb_yield( obj);
187
+ return obj;
188
+ }
189
+
190
+
191
+ /*
192
+ * Document-class: NilClass
193
+ */
194
+
195
+ /*
196
+ * call-seq:
197
+ * notempty? -> nil
198
+ *
199
+ * This spares testing for +nil+ when checking strings.
200
+ */
201
+
202
+ VALUE
203
+ rb_nil_notempty_p( VALUE str)
204
+ {
205
+ return Qnil;
206
+ }
207
+
208
+ /*
209
+ * Document-method: nonzero?
210
+ *
211
+ * call-seq:
212
+ * nonzero? -> nil
213
+ *
214
+ * This spares testing for +nil+ when checking numbers.
215
+ */
216
+
217
+ /*
218
+ * Document-method: each_line
219
+ *
220
+ * call-seq:
221
+ * each_line { |l| ... } -> nil
222
+ *
223
+ * This spares testing for +nil+ when checking strings.
224
+ */
225
+
226
+ VALUE
227
+ rb_nil_each_line( VALUE str)
228
+ {
229
+ RETURN_ENUMERATOR( str, 0, 0);
230
+ return Qnil;
231
+ }
232
+
233
+
234
+
235
+ /*
236
+ * Document-class: String
237
+ */
238
+
239
+
240
+ /*
241
+ * call-seq:
242
+ * new_string -> str
243
+ *
244
+ * Returns another string that may be modified without touching the original
245
+ * object. This means +dup+ for a string and +to_s+ for any other object.
246
+ *
247
+ */
248
+
249
+ VALUE
250
+ rb_str_new_string( VALUE obj)
251
+ {
252
+ return rb_str_dup( obj);
253
+ }
254
+
255
+
256
+ /*
257
+ * call-seq:
258
+ * notempty? -> nil or self
259
+ *
260
+ * Returns <code>self</code> if and only if <code>str</code> is not
261
+ * empty, <code>nil</code> otherwise.
262
+ *
263
+ * "hello".notempty? #=> "hello"
264
+ * "".notempty? #=> nil
265
+ */
266
+
267
+ VALUE
268
+ rb_str_notempty_p( VALUE str)
269
+ {
270
+ #if 0
271
+ /* Ruby Coding style */
272
+ if (RSTRING_LEN( str) == 0)
273
+ return Qnil;
274
+ return str;
275
+ #else
276
+ return RSTRING_LEN( str) ? str : Qnil;
277
+ #endif
278
+ }
279
+
280
+
281
+ /*
282
+ * call-seq:
283
+ * eat( n = nil) -> str
284
+ *
285
+ * Returns first <code>n</code> characters of <code>self</code> or
286
+ * whole string if <code>n</code> is <code>nil</code>. The returned
287
+ * substring will be deleted from <code>self</code>. If <code>n</code>
288
+ * is negative, characters will be eaten from the right.
289
+ *
290
+ * a = "upcase"
291
+ * a.eat 2 #=> "up"
292
+ * a #=> "case"
293
+ */
294
+
295
+ VALUE
296
+ rb_str_eat( int argc, VALUE *argv, VALUE str)
297
+ {
298
+ VALUE val;
299
+ int n;
300
+ int l;
301
+ int r;
302
+
303
+ #ifdef HAVE_HEADER_RUBY_H
304
+ n = l = RSTRING_LEN( str);
305
+ #else
306
+ n = l = rb_str_strlen( str);
307
+ #endif
308
+ if (rb_scan_args( argc, argv, "01", &val) == 1) {
309
+ if (!NIL_P( val)) {
310
+ int v = NUM2INT( val);
311
+ if (v >= 0) {
312
+ if (n >= v) n = v;
313
+ } else {
314
+ n = -n;
315
+ if (n <= v) n = v;
316
+ }
317
+ }
318
+ }
319
+ rb_str_modify( str);
320
+ #ifdef HAVE_HEADER_RUBY_H
321
+ if (n > 0) {
322
+ r = l - n;
323
+ val = rb_str_new5( str, RSTRING_PTR( str), n);
324
+ memmove( RSTRING_PTR( str), RSTRING_PTR( str) + n, r);
325
+ } else {
326
+ r = l + n;
327
+ val = rb_str_new5( str, RSTRING_PTR( str) + r, -n);
328
+ }
329
+ RSTRING_LEN( str) = r;
330
+ OBJ_INFECT( val, str);
331
+ #else
332
+ if (n > 0) {
333
+ r = 0;
334
+ } else if (n < 0) {
335
+ r = l + n;
336
+ n = -n;
337
+ } else
338
+ return Qnil;
339
+ val = rb_str_substr( str, r, n);
340
+ if (!NIL_P(val))
341
+ rb_str_update( str, r, n, rb_str_new( NULL, 0));
342
+ #endif
343
+ return val;
344
+ }
345
+
346
+
347
+ /*
348
+ * call-seq:
349
+ * cut!( length) -> str
350
+ *
351
+ * Cut string to <code>length</code>. If nothing was removed,
352
+ * <code>nil</code> is returned.
353
+ *
354
+ * a = "hello"
355
+ * a.cut! 4 #=> "hell"
356
+ * a #=> "hell"
357
+ * a.cut! 4 #=> nil
358
+ */
359
+
360
+ VALUE
361
+ rb_str_cut_bang( VALUE str, VALUE len)
362
+ {
363
+ int l;
364
+ #ifdef HAVE_HEADER_RUBY_H
365
+ #else
366
+ int n;
367
+ #endif
368
+
369
+ rb_str_modify( str);
370
+ l = NUM2INT( len);
371
+ if (l < 0)
372
+ l = 0;
373
+ #ifdef HAVE_HEADER_RUBY_H
374
+ if (l < RSTRING_LEN( str)) {
375
+ RSTRING_LEN( str) = l;
376
+ return str;
377
+ }
378
+ #else
379
+ n = rb_str_strlen( str);
380
+ if (l < n) {
381
+ rb_str_update( str, l, n - l, rb_str_new( NULL, 0));
382
+ return str;
383
+ }
384
+ #endif
385
+ return Qnil;
386
+ }
387
+
388
+
389
+ #ifdef FEATURE_STRING_CLEAR
390
+
391
+ /*
392
+ * call-seq:
393
+ * clear -> self
394
+ *
395
+ * Set to empty string. Equivalent to <code>str.replace ""</code>.
396
+ *
397
+ * a = "hello" #=> "hello"
398
+ * a.clear #=> ""
399
+ * a.empty? #=> true
400
+ */
401
+
402
+ VALUE
403
+ rb_str_clear( VALUE str)
404
+ {
405
+ rb_str_modify( str);
406
+ rb_str_resize( str, 0);
407
+ return str;
408
+ }
409
+
410
+ #endif
411
+
412
+
413
+ /*
414
+ * call-seq:
415
+ * head( n = 1) -> str
416
+ *
417
+ * Returns first <code>n</code> bytes in <code>str</code>.
418
+ *
419
+ * "hello".head( 2) #=> "he"
420
+ */
421
+
422
+ VALUE
423
+ rb_str_head( int argc, VALUE *argv, VALUE str)
424
+ {
425
+ VALUE n;
426
+ VALUE str2;
427
+ long len;
428
+
429
+ len = rb_scan_args( argc, argv, "01", &n) == 1 ? NUM2LONG( n) : 1;
430
+ return rb_str_substr( str, 0, len);
431
+ }
432
+
433
+
434
+ /*
435
+ * call-seq:
436
+ * rest( n = 1) -> str
437
+ *
438
+ * Return rest after <code>n</code> bytes in <code>str</code>.
439
+ *
440
+ * "hello".rest( 2) #=> "llo"
441
+ */
442
+
443
+ VALUE
444
+ rb_str_rest( int argc, VALUE *argv, VALUE str)
445
+ {
446
+ VALUE n;
447
+ long l, beg, len;
448
+
449
+ beg = rb_scan_args( argc, argv, "01", &n) == 1 ? NUM2LONG( n) : 1;
450
+ if (beg < 0)
451
+ beg = 0;
452
+ #ifdef HAVE_HEADER_RUBY_H
453
+ l = RSTRING_LEN( str);
454
+ #else
455
+ l = rb_str_strlen( str);
456
+ #endif
457
+ return rb_str_substr( str, beg, l - beg);
458
+ }
459
+
460
+
461
+ /*
462
+ * call-seq:
463
+ * tail( n = 1) -> str
464
+ *
465
+ * Returns last <code>n</code> bytes in <code>str</code>.
466
+ *
467
+ * "hello".tail( 2) #=> "lo"
468
+ */
469
+
470
+ VALUE
471
+ rb_str_tail( int argc, VALUE *argv, VALUE str)
472
+ {
473
+ VALUE n;
474
+ long l, beg, len;
475
+
476
+ len = rb_scan_args( argc, argv, "01", &n) == 1 ? NUM2LONG( n) : 1;
477
+ #ifdef HAVE_HEADER_RUBY_H
478
+ l = RSTRING_LEN( str);
479
+ #else
480
+ l = rb_str_strlen( str);
481
+ #endif
482
+ beg = l - len;
483
+ if (beg < 0)
484
+ beg = 0, len = l;
485
+ return rb_str_substr( str, beg, len);
486
+ }
487
+
488
+
489
+ #ifdef FEATURE_STRING_START_WITH
490
+
491
+ /*
492
+ * call-seq:
493
+ * start_with?( oth) -> true or false
494
+ *
495
+ * Checks whether the head is <code>oth</code>.
496
+ *
497
+ * "sys-apps".start_with?( "sys-") #=> true
498
+ */
499
+
500
+ VALUE
501
+ rb_str_start_with_p( VALUE str, VALUE oth)
502
+ {
503
+ return NIL_P( rb_str_starts_with_p( str, oth)) ? Qfalse : Qtrue;
504
+ }
505
+
506
+
507
+ /*
508
+ * call-seq:
509
+ * end_with?( oth) -> true or false
510
+ *
511
+ * Checks whether the tail is <code>oth</code>.
512
+ *
513
+ * "sys-apps".end_with?( "-apps") #=> true
514
+ */
515
+
516
+ VALUE
517
+ rb_str_end_with_p( VALUE str, VALUE oth)
518
+ {
519
+ return NIL_P( rb_str_ends_with_p( str, oth)) ? Qfalse : Qtrue;
520
+ }
521
+
522
+ #endif
523
+
524
+ /*
525
+ * call-seq:
526
+ * starts_with?( oth) -> nil or int
527
+ *
528
+ * Checks whether the head is <code>oth</code>. Returns length of
529
+ * <code>oth</code> when matching.
530
+ *
531
+ * "sys-apps".starts_with?( "sys-") #=> 4
532
+ *
533
+ * Caution! The Ruby 1.9.3 method #start_with? (notice the missing s)
534
+ * just returns +true+ or +false+.
535
+ */
536
+
537
+ VALUE
538
+ rb_str_starts_with_p( VALUE str, VALUE oth)
539
+ {
540
+ long i;
541
+ char *s, *o;
542
+ VALUE ost;
543
+
544
+ #ifdef HAVE_HEADER_RUBY_H
545
+ #else
546
+ if (!rb_str_comparable( str, oth))
547
+ return Qnil;
548
+ #endif
549
+ ost = rb_string_value( &oth);
550
+ i = RSTRING_LEN( ost);
551
+ if (i > RSTRING_LEN( str))
552
+ return Qnil;
553
+ s = RSTRING_PTR( str);
554
+ o = RSTRING_PTR( ost);
555
+ for (; i; i--, s++, o++) {
556
+ if (*s != *o)
557
+ return Qnil;
558
+ }
559
+ #ifdef HAVE_HEADER_RUBY_H
560
+ return INT2FIX( RSTRING_LEN( ost));
561
+ #else
562
+ return INT2FIX( rb_str_strlen( ost));
563
+ #endif
564
+ }
565
+
566
+
567
+ /*
568
+ * call-seq:
569
+ * ends_with?( oth) -> nil or int
570
+ *
571
+ * Checks whether the tail is <code>oth</code>. Returns the position
572
+ * where <code>oth</code> starts when matching.
573
+ *
574
+ * "sys-apps".ends_with?( "-apps") #=> 3
575
+ *
576
+ * Caution! The Ruby 1.9.3 method #start_with? (notice the missing s)
577
+ * just returns +true+ or +false+.
578
+ */
579
+
580
+ VALUE
581
+ rb_str_ends_with_p( VALUE str, VALUE oth)
582
+ {
583
+ long i;
584
+ char *s, *o;
585
+ VALUE ost;
586
+
587
+ #ifdef HAVE_HEADER_RUBY_H
588
+ #else
589
+ if (!rb_str_comparable( str, oth))
590
+ return Qnil;
591
+ #endif
592
+ ost = rb_string_value( &oth);
593
+ i = RSTRING_LEN( ost);
594
+ if (i > RSTRING_LEN( str))
595
+ return Qnil;
596
+ s = RSTRING_END( str);
597
+ o = RSTRING_END( ost);
598
+ for (; i; i--)
599
+ if (*--s != *--o)
600
+ return Qnil;
601
+ #ifdef HAVE_HEADER_RUBY_H
602
+ return INT2FIX( RSTRING_LEN( str) - RSTRING_LEN( ost));
603
+ #else
604
+ return INT2FIX( rb_str_strlen( str) - rb_str_strlen( ost));
605
+ #endif
606
+ }
607
+
608
+ #ifdef FEATURE_STRING_ORD
609
+
610
+ /*
611
+ * call-seq:
612
+ * ord() -> nil or int
613
+ *
614
+ * Returns the ASCII value of the first character, if any.
615
+ *
616
+ * Caution! For UTF-8 characters, this will return the first byte's value.
617
+ * This will do no harm in Ruby 1.8 but the standard Ruby 1.9 +String#ord+
618
+ * returns the first codepoint. Please do not overwrite that method.
619
+ */
620
+
621
+ VALUE rb_str_ord( VALUE str)
622
+ {
623
+ return RSTRING_LEN( str) > 0 ? INT2FIX( RSTRING_PTR( str)[ 0]) : Qnil;
624
+ }
625
+
626
+ #endif
627
+
628
+ /*
629
+ * call-seq:
630
+ * axe( n = 80) -> str
631
+ *
632
+ * Cut off everthing beyond then <code>n</code>th character. Replace the
633
+ * last bytes by ellipses.
634
+ *
635
+ * a = "Redistribution and use in source and binary forms, with or without"
636
+ * a.axe( 16) #=> "Redistributio..."
637
+ */
638
+
639
+ VALUE
640
+ rb_str_axe( int argc, VALUE *argv, VALUE str)
641
+ {
642
+ VALUE n;
643
+ VALUE ret;
644
+ long newlen, oldlen;
645
+
646
+ if (rb_scan_args( argc, argv, "01", &n) == 1 && !NIL_P( n))
647
+ newlen = NUM2LONG( n);
648
+ else
649
+ newlen = 80;
650
+ if (newlen < 0)
651
+ return Qnil;
652
+
653
+ #ifdef HAVE_HEADER_RUBY_H
654
+ oldlen = RSTRING_LEN( str);
655
+ #else
656
+ oldlen = rb_str_strlen( str);
657
+ #endif
658
+ if (newlen < oldlen) {
659
+ VALUE ell;
660
+ long e;
661
+
662
+ ell = rb_str_new2( "...");
663
+ #ifdef HAVE_HEADER_RUBY_H
664
+ e = RSTRING_LEN( ell);
665
+ #else
666
+ e = rb_str_strlen( ell);
667
+ #endif
668
+ if (newlen > e) {
669
+ ret = rb_str_substr( str, 0, newlen - e);
670
+ rb_str_append( ret, ell);
671
+ } else
672
+ ret = rb_str_substr( str, 0, newlen);
673
+ OBJ_INFECT( ret, str);
674
+ } else
675
+ ret = str;
676
+ return ret;
677
+ }
678
+
679
+
680
+ /*
681
+ * Document-class: Numeric
682
+ */
683
+
684
+ /*
685
+ * call-seq:
686
+ * pos? -> true or false
687
+ *
688
+ * Check whether +num+ is positive.
689
+ *
690
+ */
691
+
692
+ VALUE
693
+ rb_num_pos_p( VALUE num)
694
+ {
695
+ VALUE r = Qfalse;
696
+
697
+ switch (TYPE( num)) {
698
+ case T_FIXNUM:
699
+ if (NUM2LONG( num) > 0)
700
+ r = Qtrue;
701
+ break;
702
+
703
+ case T_BIGNUM:
704
+ if (RBIGNUM_SIGN( num)) /* 0 is not a Bignum. */
705
+ r = Qtrue;
706
+ break;
707
+
708
+ case T_FLOAT:
709
+ if (RFLOAT_VALUE( num) > 0.0)
710
+ r = Qtrue;
711
+ break;
712
+
713
+ default:
714
+ return rb_num_neg_p( rb_funcall( INT2FIX( 0), id_cmp, 1, num));
715
+ break;
716
+ }
717
+ return r;
718
+ }
719
+
720
+ /*
721
+ * call-seq:
722
+ * neg? -> true or false
723
+ *
724
+ * Check whether +num+ is negative.
725
+ *
726
+ */
727
+
728
+ VALUE
729
+ rb_num_neg_p( VALUE num)
730
+ {
731
+ VALUE r = Qfalse;
732
+
733
+ switch (TYPE( num)) {
734
+ case T_FIXNUM:
735
+ if (NUM2LONG( num) < 0)
736
+ r = Qtrue;
737
+ break;
738
+
739
+ case T_BIGNUM:
740
+ if (!RBIGNUM_SIGN( num)) /* 0 is not a Bignum. */
741
+ r = Qtrue;
742
+ break;
743
+
744
+ case T_FLOAT:
745
+ if (RFLOAT_VALUE( num) < 0.0)
746
+ r = Qtrue;
747
+ break;
748
+
749
+ default:
750
+ return rb_num_neg_p( rb_funcall( num, id_cmp, 1, INT2FIX( 0)));
751
+ break;
752
+ }
753
+ return r;
754
+ }
755
+
756
+ /*
757
+ * call-seq:
758
+ * grammatical sing, plu -> str
759
+ *
760
+ * Singular or plural
761
+ *
762
+ * 1.grammatical "line", "lines" #=> "line"
763
+ * 6.grammatical "child", "children" #=> "children"
764
+ */
765
+
766
+ VALUE
767
+ rb_num_grammatical( VALUE num, VALUE sing, VALUE plu)
768
+ {
769
+ long l;
770
+ double d;
771
+
772
+ switch (TYPE( num)) {
773
+ case T_FIXNUM:
774
+ l = NUM2LONG( num);
775
+ if (l == 1l || l == -1l)
776
+ return sing;
777
+ break;
778
+
779
+ case T_BIGNUM:
780
+ /* 1 is not a Bignum */
781
+ break;
782
+
783
+ case T_FLOAT:
784
+ d = RFLOAT_VALUE( num);
785
+ if (d == 1.0 || d == -1.0)
786
+ return sing;
787
+ break;
788
+
789
+ default:
790
+ l = NUM2LONG( rb_funcall( num, id_cmp, 1, INT2FIX( 1)));
791
+ if (l == 0)
792
+ return sing;
793
+ l = NUM2LONG( rb_funcall( num, id_cmp, 1, INT2FIX(-1)));
794
+ if (l == 0)
795
+ return sing;
796
+ break;
797
+ }
798
+ return plu;
799
+ }
800
+
801
+
802
+ /*
803
+ * call-seq:
804
+ * num.sqrt -> num
805
+ *
806
+ * Square root.
807
+ *
808
+ * 144.sqrt #=> 12.0
809
+ */
810
+
811
+ VALUE
812
+ rb_num_sqrt( VALUE num)
813
+ {
814
+ return rb_float_new( sqrt( RFLOAT_VALUE( rb_Float( num))));
815
+ }
816
+
817
+ /*
818
+ * call-seq:
819
+ * num.cbrt -> num
820
+ *
821
+ * Cube root.
822
+ *
823
+ * 27.cbrt #=> 3.0
824
+ */
825
+
826
+ VALUE
827
+ rb_num_cbrt( VALUE num)
828
+ {
829
+ double n;
830
+ int neg;
831
+ int i;
832
+
833
+ n = RFLOAT_VALUE( rb_Float( num));
834
+ if ((neg = n < 0))
835
+ n = -n;
836
+ n = sqrt( sqrt( n));
837
+ i = 2;
838
+ for (;;) {
839
+ double w = n;
840
+ int j;
841
+
842
+ for (j=i;j;--j) w = sqrt( w);
843
+ i *= 2;
844
+ w *= n;
845
+ if (n == w) break;
846
+ n = w;
847
+ }
848
+ return rb_float_new( neg ? -n : n);
849
+ }
850
+
851
+
852
+ /*
853
+ * Document-class: Array
854
+ */
855
+
856
+ /*
857
+ * call-seq:
858
+ * notempty? -> nil or self
859
+ *
860
+ * Returns <code>self</code> if and only if <code>ary</code> is not
861
+ * empty, <code>nil</code> otherwise.
862
+ *
863
+ * %w(a b).notempty? #=> [ "a", "b"]
864
+ * [].notempty? #=> nil
865
+ */
866
+
867
+ VALUE
868
+ rb_ary_notempty_p( VALUE ary)
869
+ {
870
+ return RARRAY_LEN( ary) == 0 ? Qnil : ary;
871
+ }
872
+
873
+
874
+ /*
875
+ * call-seq:
876
+ * indexes() -> ary
877
+ * keys() -> ary
878
+ *
879
+ * Returns the indexes from <code>0</code> to
880
+ * <code>array.length()</code> as an array.
881
+ *
882
+ * [ "a", "h", "q"].indexes #=> [ 0, 1, 2]
883
+ */
884
+
885
+ VALUE
886
+ rb_ary_indexes( VALUE ary)
887
+ {
888
+ VALUE ret;
889
+ int i, j;
890
+
891
+ j = RARRAY_LEN( ary);
892
+ ret = rb_ary_new2( j);
893
+ for (i = 0; j; ++i, --j) {
894
+ rb_ary_push( ret, INT2FIX( i));
895
+ }
896
+ return ret;
897
+ }
898
+
899
+
900
+ /*
901
+ * call-seq:
902
+ * pick { |elem| ... } -> obj or nil
903
+ *
904
+ * Deletes the element where the <em>block</em> first returns
905
+ * <code>true</code>. Or <code>nil</code> if nothing is found.
906
+ *
907
+ * a = %w(ant bat cat dog)
908
+ * a.pick { |e| e =~ /^c/ } #=> "cat"
909
+ * a #=> ["ant", "bat", "dog"]
910
+ * a.pick { |e| e =~ /^x/ } #=> nil
911
+ */
912
+
913
+ VALUE
914
+ rb_ary_pick( VALUE ary)
915
+ {
916
+ VALUE pos;
917
+
918
+ pos = supplement_index_blk( ary);
919
+ if (!NIL_P( pos))
920
+ return rb_funcall( ary, id_delete_at, 1, pos);
921
+ return Qnil;
922
+ }
923
+
924
+ VALUE
925
+ supplement_index_blk( VALUE ary)
926
+ {
927
+ long i, j;
928
+
929
+ for (i = 0, j = RARRAY_LEN( ary); j > 0; i++, j--) {
930
+ if (RTEST( rb_yield( RARRAY_PTR( ary)[ i])))
931
+ return LONG2NUM( i);
932
+ }
933
+ return Qnil;
934
+ }
935
+
936
+ /*
937
+ * call-seq:
938
+ * rpick { |elem| ... } -> obj or nil
939
+ *
940
+ * Deletes the element where the <em>block</em> first returns
941
+ * <code>true</code>. Or <code>nil</code> if nothing is found. Search
942
+ * from right to left.
943
+ *
944
+ * a = %w(ant cow bat cat dog)
945
+ * a.rpick { |e| e =~ /^c/ } #=> "cat"
946
+ * a #=> ["ant", "cow", "bat", "dog"]
947
+ * a.rpick { |e| e =~ /^x/ } #=> nil
948
+ */
949
+
950
+ VALUE
951
+ rb_ary_rpick( VALUE ary)
952
+ {
953
+ VALUE pos;
954
+
955
+ pos = supplement_rindex_blk( ary);
956
+ if (!NIL_P( pos))
957
+ return rb_funcall( ary, id_delete_at, 1, pos);
958
+ return Qnil;
959
+ }
960
+
961
+ VALUE
962
+ supplement_rindex_blk( VALUE ary)
963
+ {
964
+ long i;
965
+
966
+ for (i = RARRAY_LEN( ary); i;) {
967
+ --i;
968
+ if (rb_yield( RARRAY_PTR( ary)[ i]))
969
+ return LONG2NUM( i);
970
+ }
971
+ return Qnil;
972
+ }
973
+
974
+
975
+ #ifdef FEATURE_ARRAY_INDEX_WITH_BLOCK
976
+
977
+ /*
978
+ * call-seq:
979
+ * index( obj) -> int or nil
980
+ * index() { |elem| ... } -> int or nil
981
+ *
982
+ * Returns the index of the first object in <code>self</code> such that
983
+ * is <code>==</code> to <code>obj</code> or the <em>block</em> returns
984
+ * <code>true</code>. If no match is found, <code>nil</code> is
985
+ * returned.
986
+ *
987
+ * a = %w(a b c d e)
988
+ * a.index("b") #=> 1
989
+ * a.index("z") #=> nil
990
+ * a.index { |e| e >= "b" } #=> 1
991
+ * a.index { |e| e >= "q" } #=> nil
992
+ */
993
+
994
+ VALUE
995
+ rb_ary_index( int argc, VALUE *argv, VALUE ary)
996
+ {
997
+ VALUE val;
998
+
999
+ if (rb_scan_args( argc, argv, "01", &val) == 1) {
1000
+ if (rb_block_given_p())
1001
+ rb_warning( "given block not used");
1002
+ return supplement_index_val( ary, val);
1003
+ } else
1004
+ return supplement_index_blk( ary);
1005
+ return Qnil;
1006
+ }
1007
+
1008
+ VALUE
1009
+ supplement_index_val( VALUE ary, VALUE val)
1010
+ {
1011
+ long i;
1012
+
1013
+ for (i = 0; i < RARRAY_LEN( ary); i++)
1014
+ if (rb_equal( RARRAY_PTR( ary)[ i], val))
1015
+ return LONG2NUM( i);
1016
+ return Qnil;
1017
+ }
1018
+
1019
+
1020
+ /*
1021
+ * call-seq:
1022
+ * rindex( obj) -> int or nil
1023
+ * rindex() { |elem| ... } -> int or nil
1024
+ *
1025
+ * Returns the index of the first object in <code>self</code> such that
1026
+ * is <code>==</code> to <code>obj</code> or the <em>block</em> returns
1027
+ * <code>true</code>. If no match is found, <code>nil</code> is
1028
+ * returned. Search from right to left.
1029
+ *
1030
+ * a = %w(a b c d e)
1031
+ * a.rindex("b") #=> 1
1032
+ * a.rindex("z") #=> nil
1033
+ * a.rindex { |e| e >= "b" } #=> 4
1034
+ * a.rindex { |e| e >= "q" } #=> nil
1035
+ */
1036
+
1037
+ VALUE
1038
+ rb_ary_rindex( int argc, VALUE *argv, VALUE ary)
1039
+ {
1040
+ VALUE val;
1041
+
1042
+ if (rb_scan_args( argc, argv, "01", &val) == 1) {
1043
+ if (rb_block_given_p())
1044
+ rb_warning( "given block not used");
1045
+ return supplement_rindex_val( ary, val);
1046
+ } else
1047
+ return supplement_rindex_blk( ary);
1048
+ return Qnil;
1049
+ }
1050
+
1051
+ VALUE
1052
+ supplement_rindex_val( VALUE ary, VALUE val)
1053
+ {
1054
+ long i;
1055
+
1056
+ for (i = RARRAY_LEN( ary); i;)
1057
+ if (rb_equal( RARRAY_PTR( ary)[ --i], val))
1058
+ return LONG2NUM( i);
1059
+ return Qnil;
1060
+ }
1061
+
1062
+ #endif
1063
+
1064
+ #ifdef FEATURE_ARRAY_SELECT_BANG
1065
+
1066
+ /*
1067
+ * Document-method: select!
1068
+ *
1069
+ * call-seq:
1070
+ * select! { |x| ... } -> ary
1071
+ *
1072
+ * Remove all items for that the block returns +nil+ or +false+.
1073
+ *
1074
+ */
1075
+
1076
+ VALUE
1077
+ rb_ary_select_bang( VALUE self)
1078
+ {
1079
+ return rb_iterate( &supplement_reject, self,
1080
+ &supplement_invert_yield, Qnil);
1081
+ }
1082
+
1083
+ VALUE
1084
+ supplement_reject( VALUE obj)
1085
+ {
1086
+ if (!id_reject_bang)
1087
+ id_reject_bang = rb_intern( "reject!");
1088
+ return rb_funcall( obj, id_reject_bang, 0);
1089
+ }
1090
+
1091
+ VALUE
1092
+ supplement_invert_yield( VALUE elem)
1093
+ {
1094
+ return NIL_P( rb_yield( elem)) ? Qtrue : Qfalse;
1095
+ }
1096
+
1097
+ #endif
1098
+
1099
+
1100
+
1101
+ /*
1102
+ * Document-class: Hash
1103
+ */
1104
+
1105
+ /*
1106
+ * call-seq:
1107
+ * notempty? -> nil or self
1108
+ *
1109
+ * Returns <code>self</code> if and only if <code>hash</code> is not
1110
+ * empty, <code>nil</code> otherwise.
1111
+ *
1112
+ * { :a => "A"}.notempty? #=> { :a => "A"}
1113
+ * {}.notempty? #=> nil
1114
+ */
1115
+
1116
+ VALUE
1117
+ rb_hash_notempty_p( VALUE hash)
1118
+ {
1119
+ return RHASH_SIZE( hash) == 0 ? Qnil : hash;
1120
+ }
1121
+
1122
+
1123
+ /*
1124
+ * Document-class: File
1125
+ */
1126
+
1127
+ /*
1128
+ * call-seq:
1129
+ * size -> integer
1130
+ *
1131
+ * Returns <code>file</code>'s size. A shortcut for
1132
+ * <code>file.stat.size</code>. This constitutes consistency with
1133
+ * <code>StringIO</code>.
1134
+ *
1135
+ * file.size #=> 16384
1136
+ */
1137
+
1138
+ VALUE
1139
+ rb_file_size( VALUE obj)
1140
+ {
1141
+ #ifdef HAVE_HEADER_RUBY_H
1142
+ OpenFile *fptr;
1143
+ #else
1144
+ rb_io_t *fptr;
1145
+ #endif
1146
+ struct stat st;
1147
+
1148
+ GetOpenFile( obj, fptr);
1149
+ #ifdef HAVE_HEADER_RUBY_H
1150
+ if (fstat( fileno( fptr->f), &st) == -1) {
1151
+ rb_sys_fail( fptr->path);
1152
+ }
1153
+ #else
1154
+ if (fstat( fptr->fd, &st) == -1) {
1155
+ rb_sys_fail_str( fptr->pathv);
1156
+ }
1157
+ #endif
1158
+ return INT2FIX( st.st_size);
1159
+ }
1160
+
1161
+
1162
+ /*
1163
+ * call-seq:
1164
+ * flockb( excl = nil, nb = nil) { || ... } -> nil
1165
+ *
1166
+ * Lock file using the <code>flock()</code> system call.
1167
+ * When the <code>nb</code> flag is <code>true</code>, the method
1168
+ * won't block but rather raise an exception. Catch
1169
+ * <code>SystemCallError</code>. The calls may be nested in any order.
1170
+ *
1171
+ * File.open "/var/mail/joe", "a" do |f|
1172
+ * f.flockb true do
1173
+ * f.write another_message
1174
+ * end
1175
+ * end
1176
+ */
1177
+
1178
+ VALUE
1179
+ rb_file_flockb( int argc, VALUE *argv, VALUE file)
1180
+ {
1181
+ VALUE excl, nb;
1182
+ struct supplement_flock cur_flock;
1183
+ #ifdef HAVE_HEADER_RUBY_H
1184
+ OpenFile *fptr;
1185
+ #else
1186
+ rb_io_t *fptr;
1187
+ #endif
1188
+ int op;
1189
+
1190
+ rb_scan_args( argc, argv, "02", &excl, &nb);
1191
+ supplement_init_flock( &cur_flock, file, excl);
1192
+
1193
+ op = cur_flock.op | LOCK_NB;
1194
+ GetOpenFile( file, fptr);
1195
+ #ifdef HAVE_HEADER_RUBY_H
1196
+ while (flock( fileno( fptr->f), op) < 0) {
1197
+ #else
1198
+ while (flock( fptr->fd, op) < 0) {
1199
+ #endif
1200
+ switch (errno) {
1201
+ case EAGAIN:
1202
+ case EACCES:
1203
+ #if defined( EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
1204
+ case EWOULDBLOCK:
1205
+ #endif
1206
+ if (!RTEST( nb)) {
1207
+ rb_thread_polling(); /* busy wait */
1208
+ rb_io_check_closed( fptr);
1209
+ continue;
1210
+ }
1211
+ /* fall through */
1212
+ default:
1213
+ #ifdef HAVE_HEADER_RUBY_H
1214
+ rb_sys_fail( fptr->path);
1215
+ #else
1216
+ rb_sys_fail_str( fptr->pathv);
1217
+ #endif
1218
+ }
1219
+ }
1220
+ cur_flock.prev = flocks_root;
1221
+ flocks_root = &cur_flock;
1222
+ return rb_ensure( rb_yield, Qnil, supplement_do_unflock, Qnil);
1223
+ }
1224
+
1225
+ void
1226
+ supplement_init_flock( struct supplement_flock *s, VALUE file, VALUE excl)
1227
+ {
1228
+ struct supplement_flock *i;
1229
+
1230
+ s->file = file;
1231
+
1232
+ s->last_op = LOCK_UN;
1233
+ for (i = flocks_root; i != NULL; i = i->prev) {
1234
+ if (i->file == file) {
1235
+ s->last_op = i->op;
1236
+ break;
1237
+ }
1238
+ }
1239
+
1240
+ switch (s->last_op) {
1241
+ case LOCK_UN:
1242
+ case LOCK_SH:
1243
+ s->op = RTEST( excl) ? LOCK_EX : LOCK_SH;
1244
+ break;
1245
+ case LOCK_EX:
1246
+ s->op = LOCK_EX;
1247
+ break;
1248
+ default:
1249
+ s->op = LOCK_UN; /* should never be reached. */
1250
+ break;
1251
+ }
1252
+ }
1253
+
1254
+ VALUE
1255
+ supplement_do_unflock( VALUE v)
1256
+ {
1257
+ #ifdef HAVE_HEADER_RUBY_H
1258
+ OpenFile *fptr;
1259
+ #else
1260
+ rb_io_t *fptr;
1261
+ #endif
1262
+ int fd;
1263
+
1264
+ GetOpenFile( flocks_root->file, fptr);
1265
+ #ifdef HAVE_HEADER_RUBY_H
1266
+ flock( fileno( fptr->f), flocks_root->last_op);
1267
+ #else
1268
+ flock( fptr->fd, flocks_root->last_op);
1269
+ #endif
1270
+ flocks_root = flocks_root->prev;
1271
+
1272
+ return Qnil;
1273
+ }
1274
+
1275
+
1276
+ /*
1277
+ * call-seq:
1278
+ * umask() -> int
1279
+ * umask( int) -> int
1280
+ * umask( int) { ... } -> obj
1281
+ *
1282
+ * Returns the current umask value for this process. If the optional
1283
+ * argument is given, set the umask to that value and return the
1284
+ * previous value. If a block is given, the umask value will be
1285
+ * reset and the blocks value is returned.
1286
+ *
1287
+ * Umask values are <em>subtracted</em> from the default permissions,
1288
+ * so a umask of <code>0222</code> would make a file read-only for
1289
+ * everyone.
1290
+ *
1291
+ * File.umask( 0006) #=> 18
1292
+ * File.umask #=> 6
1293
+ */
1294
+
1295
+ VALUE
1296
+ rb_file_s_umask( int argc, VALUE *argv)
1297
+ {
1298
+ int omask = 0;
1299
+
1300
+ rb_secure( 2);
1301
+ switch (argc) {
1302
+ case 0:
1303
+ omask = umask( 0777);
1304
+ umask( omask);
1305
+ break;
1306
+ case 1:
1307
+ omask = umask( NUM2INT( argv[ 0]));
1308
+ if (rb_block_given_p())
1309
+ return rb_ensure( rb_yield, Qnil,
1310
+ supplement_do_unumask, INT2FIX( omask));
1311
+ break;
1312
+ default:
1313
+ rb_raise( rb_eArgError,
1314
+ "wrong number of arguments (%d for 0..1)", argc);
1315
+ }
1316
+ return INT2FIX( omask);
1317
+ }
1318
+
1319
+ VALUE
1320
+ supplement_do_unumask( VALUE v)
1321
+ {
1322
+ umask( NUM2INT( v));
1323
+ return Qnil;
1324
+ }
1325
+
1326
+
1327
+ /*
1328
+ * Document-class: Dir
1329
+ */
1330
+
1331
+ /*
1332
+ * call-seq:
1333
+ * current() -> dir
1334
+ *
1335
+ * Current directory as a Dir object.
1336
+ *
1337
+ */
1338
+
1339
+ VALUE
1340
+ rb_dir_s_current( VALUE dir)
1341
+ {
1342
+ return rb_funcall( dir, rb_intern( "new"), 1, rb_str_new( ".", 1));
1343
+ }
1344
+
1345
+
1346
+ /*
1347
+ * call-seq:
1348
+ * mkdir!( path, modes = nil) -> str or nil
1349
+ *
1350
+ * Make a directory and all subdirectories if needed.
1351
+ *
1352
+ * If you specifiy modes, be sure that you have the permission to create
1353
+ * subdirectories.
1354
+ *
1355
+ * Returns the path demanded if the directory was created and +nil+ if
1356
+ * it existed before.
1357
+ *
1358
+ */
1359
+
1360
+ VALUE
1361
+ rb_dir_s_mkdir_bang( int argc, VALUE *argv)
1362
+ {
1363
+ VALUE path, modes;
1364
+
1365
+ rb_scan_args( argc, argv, "11", &path, &modes);
1366
+ if (!rb_file_directory_p( rb_cFile, path)) {
1367
+ VALUE parent[2];
1368
+
1369
+ parent[ 0] = rb_file_dirname( path);
1370
+ parent[ 1] = modes;
1371
+ rb_dir_s_mkdir_bang( 2, parent);
1372
+ if (!id_mkdir)
1373
+ id_mkdir = rb_intern( "mkdir");
1374
+ if (NIL_P(modes))
1375
+ rb_funcall( rb_cDir, id_mkdir, 1, path);
1376
+ else
1377
+ rb_funcall( rb_cDir, id_mkdir, 2, path, modes);
1378
+ return path;
1379
+ }
1380
+ return Qnil;
1381
+ }
1382
+
1383
+
1384
+ /*
1385
+ * call-seq:
1386
+ * entries!() -> dir
1387
+ *
1388
+ * Entries without <code>"."</code> and <code>".."</code>.
1389
+ *
1390
+ */
1391
+
1392
+ VALUE
1393
+ rb_dir_entries_bang( VALUE self)
1394
+ {
1395
+ VALUE e;
1396
+
1397
+ e = rb_funcall( self, rb_intern( "entries"), 0);
1398
+ rb_ary_delete( e, rb_str_new( ".", 1));
1399
+ rb_ary_delete( e, rb_str_new( "..", 2));
1400
+ return e;
1401
+ }
1402
+
1403
+
1404
+ /*
1405
+ * call-seq:
1406
+ * chdir() -> nil
1407
+ * chdir() { |path| ... } -> obj
1408
+ *
1409
+ * As you probably expect, change the working directory like in
1410
+ * <code>Dir.chdir</code>.
1411
+ *
1412
+ */
1413
+
1414
+ VALUE
1415
+ rb_dir_chdir( VALUE dir)
1416
+ {
1417
+ VALUE path;
1418
+
1419
+ if (!id_chdir) {
1420
+ id_chdir = rb_intern( "chdir");
1421
+ id_path = rb_intern( "path");
1422
+ }
1423
+ path = rb_funcall( dir, id_path, 0);
1424
+ if (rb_block_given_p())
1425
+ return rb_iterate( &supplement_chdir, path, &rb_yield, Qnil);
1426
+ else {
1427
+ supplement_chdir( path);
1428
+ return Qnil;
1429
+ }
1430
+ }
1431
+
1432
+ VALUE
1433
+ supplement_chdir( VALUE path)
1434
+ {
1435
+ return rb_funcall( rb_cDir, id_chdir, 1, path);
1436
+ }
1437
+
1438
+
1439
+ /*
1440
+ * Document-class: Match
1441
+ */
1442
+
1443
+ /*
1444
+ * call-seq:
1445
+ * begin( n = nil) -> integer
1446
+ *
1447
+ * Returns the offset of the start of the <code>n</code>th element of
1448
+ * the match array in the string. In case <code>n</code> is
1449
+ * <code>nil</code> the 0th match (whole) is assumed.
1450
+ *
1451
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138.")
1452
+ * m.begin( 0) #=> 1
1453
+ * m.begin #=> 1
1454
+ * m.begin( 2) #=> 2
1455
+ */
1456
+
1457
+ VALUE
1458
+ rb_match_begin( int argc, VALUE *argv, VALUE match)
1459
+ {
1460
+ VALUE val;
1461
+ int i;
1462
+
1463
+ if (rb_scan_args( argc, argv, "01", &val) == 1) {
1464
+ i = NIL_P( val) ? 0 : NUM2INT( val);
1465
+ } else
1466
+ i = 0;
1467
+
1468
+ if (i < 0 || RMATCH_REGS( match)->num_regs <= i)
1469
+ rb_raise( rb_eIndexError, "index %d out of matches", i);
1470
+
1471
+ if (RMATCH_REGS( match)->beg[i] < 0)
1472
+ return Qnil;
1473
+
1474
+ return INT2FIX( RMATCH_REGS( match)->beg[i]);
1475
+ }
1476
+
1477
+
1478
+ /*
1479
+ * call-seq:
1480
+ * end( n = nil) -> integer
1481
+ *
1482
+ * Returns the offset of the character immediately following the end of
1483
+ * the <code>n</code>th element of the match array in the string. In
1484
+ * case <code>n</code> is <code>nil</code> the 0th match (whole) is
1485
+ * assumed.
1486
+ *
1487
+ * m = /(.)(.)(\d+)(\d)/.match("THX1138.")
1488
+ * m.end( 0) #=> 7
1489
+ * m.end #=> 7
1490
+ * m.end( 2) #=> 3
1491
+ */
1492
+
1493
+ VALUE
1494
+ rb_match_end( int argc, VALUE *argv, VALUE match)
1495
+ {
1496
+ VALUE val;
1497
+ int i;
1498
+
1499
+ if (rb_scan_args( argc, argv, "01", &val) == 1) {
1500
+ i = NIL_P( val) ? 0 : NUM2INT( val);
1501
+ } else
1502
+ i = 0;
1503
+
1504
+ if (i < 0 || RMATCH_REGS( match)->num_regs <= i)
1505
+ rb_raise( rb_eIndexError, "index %d out of matches", i);
1506
+
1507
+ if (RMATCH_REGS( match)->beg[i] < 0)
1508
+ return Qnil;
1509
+
1510
+ return INT2FIX( RMATCH_REGS( match)->end[i]);
1511
+ }
1512
+
1513
+
1514
+ #ifdef FEATURE_THREAD_EXCLUSIVE
1515
+
1516
+ /*
1517
+ * Document-class: Thread
1518
+ */
1519
+
1520
+ /*
1521
+ * call-seq:
1522
+ * Thread.exclusive { ... } -> obj
1523
+ *
1524
+ * Sets the global ``thread critical'' condition temporarily. Return
1525
+ * value is the object returned by the <em>block</em>.
1526
+ */
1527
+
1528
+ VALUE
1529
+ rb_thread_exclusive( void)
1530
+ {
1531
+ VALUE old_tc = rb_thread_critical;
1532
+
1533
+ rb_thread_critical = Qtrue;
1534
+ return rb_ensure( rb_yield, Qnil, bsruby_set_thread_critical, old_tc);
1535
+ }
1536
+
1537
+ VALUE
1538
+ bsruby_set_thread_critical( VALUE c)
1539
+ {
1540
+ rb_thread_critical = c;
1541
+ return Qnil;
1542
+ }
1543
+
1544
+ #endif
1545
+
1546
+
1547
+
1548
+ /*
1549
+ * Document-class: Struct
1550
+ */
1551
+
1552
+ /*
1553
+ * Document-method: []
1554
+ *
1555
+ * call-seq:
1556
+ * Struct[ ...] -> cls
1557
+ *
1558
+ * Alias for Struct.new; This applies to Struct subclass generation
1559
+ * as well as to the subclasses instance creation.
1560
+ *
1561
+ * S = Struct[ :a, :b] #=> S
1562
+ * s = S[ 'A', 'B'] #=> #<struct S a="A", b="B">
1563
+ *
1564
+ */
1565
+
1566
+
1567
+ void Init_supplement( void)
1568
+ {
1569
+ rb_define_alias( rb_cObject, "cls", "class");
1570
+ rb_define_method( rb_cObject, "new_string", rb_obj_new_string, 0);
1571
+ rb_define_method( rb_cObject, "nil_if", rb_obj_nil_if, 1);
1572
+ #ifdef FEATURE_KERNEL_TAP
1573
+ rb_define_method( rb_mKernel, "tap", rb_krn_tap, 0);
1574
+ #endif
1575
+ rb_define_method( rb_mKernel, "tap!", rb_krn_tap_bang, 0);
1576
+
1577
+ rb_define_method( rb_cNilClass, "notempty?", rb_nil_notempty_p, 0);
1578
+ rb_define_method( rb_cNilClass, "nonzero?", rb_nil_notempty_p, 0);
1579
+ rb_define_method( rb_cNilClass, "each_line", rb_nil_each_line, 0);
1580
+
1581
+ rb_define_method( rb_cString, "new_string", rb_str_new_string, 0);
1582
+ rb_define_method( rb_cString, "notempty?", rb_str_notempty_p, 0);
1583
+ rb_define_method( rb_cString, "eat", rb_str_eat, -1);
1584
+ rb_define_method( rb_cString, "cut!", rb_str_cut_bang, 1);
1585
+ #ifdef FEATURE_STRING_CLEAR
1586
+ rb_define_method( rb_cString, "clear", rb_str_clear, 0);
1587
+ #endif
1588
+ rb_define_method( rb_cString, "head", rb_str_head, -1);
1589
+ rb_define_method( rb_cString, "rest", rb_str_rest, -1);
1590
+ rb_define_method( rb_cString, "tail", rb_str_tail, -1);
1591
+ #ifdef FEATURE_STRING_START_WITH
1592
+ rb_define_method( rb_cString, "start_with?", rb_str_start_with_p, 1);
1593
+ rb_define_method( rb_cString, "end_with?", rb_str_end_with_p, 1);
1594
+ #endif
1595
+ rb_define_method( rb_cString, "starts_with?", rb_str_starts_with_p, 1);
1596
+ rb_define_method( rb_cString, "ends_with?", rb_str_ends_with_p, 1);
1597
+ rb_define_alias( rb_cString, "starts_with", "start_with?");
1598
+ rb_define_alias( rb_cString, "ends_with", "end_with?");
1599
+ #ifdef FEATURE_STRING_ORD
1600
+ rb_define_method( rb_cString, "ord", rb_str_ord, 0);
1601
+ #endif
1602
+ rb_define_method( rb_cString, "axe", rb_str_axe, -1);
1603
+
1604
+ rb_define_method( rb_cNumeric, "pos?", rb_num_pos_p, 0);
1605
+ rb_define_method( rb_cNumeric, "neg?", rb_num_neg_p, 0);
1606
+ rb_define_method( rb_cNumeric, "grammatical", rb_num_grammatical, 2);
1607
+ rb_define_method( rb_cNumeric, "sqrt", rb_num_sqrt, 0);
1608
+ rb_define_method( rb_cNumeric, "cbrt", rb_num_cbrt, 0);
1609
+
1610
+ rb_define_method( rb_cArray, "notempty?", rb_ary_notempty_p, 0);
1611
+ rb_define_method( rb_cArray, "indexes", rb_ary_indexes, 0);
1612
+ rb_define_alias( rb_cArray, "keys", "indexes");
1613
+ rb_define_method( rb_cArray, "pick", rb_ary_pick, 0);
1614
+ rb_define_method( rb_cArray, "rpick", rb_ary_rpick, 0);
1615
+ #ifdef FEATURE_ARRAY_INDEX_WITH_BLOCK
1616
+ rb_define_method( rb_cArray, "index", rb_ary_index, -1);
1617
+ rb_define_method( rb_cArray, "rindex", rb_ary_rindex, -1);
1618
+ #endif
1619
+ #ifdef FEATURE_ARRAY_SELECT_BANG
1620
+ rb_define_method( rb_cArray, "select!", rb_ary_select_bang, 0);
1621
+ #endif
1622
+
1623
+ rb_define_method( rb_cHash, "notempty?", rb_hash_notempty_p, 0);
1624
+
1625
+ rb_define_method( rb_cFile, "size", rb_file_size, 0);
1626
+ rb_define_method( rb_cFile, "flockb", rb_file_flockb, -1);
1627
+ rb_define_singleton_method( rb_cFile, "umask", rb_file_s_umask, -1);
1628
+
1629
+ rb_define_singleton_method( rb_cDir, "current", rb_dir_s_current, 0);
1630
+ rb_define_singleton_method( rb_cDir, "mkdir!", rb_dir_s_mkdir_bang, -1);
1631
+ rb_define_method( rb_cDir, "entries!", rb_dir_entries_bang, 0);
1632
+ rb_define_method( rb_cDir, "chdir", rb_dir_chdir, 0);
1633
+
1634
+ rb_define_method( rb_cMatch, "begin", rb_match_begin, -1);
1635
+ rb_define_method( rb_cMatch, "end", rb_match_end, -1);
1636
+
1637
+ #ifdef FEATURE_THREAD_EXCLUSIVE
1638
+ rb_define_singleton_method( rb_cThread, "exclusive",
1639
+ rb_thread_exclusive, 0);
1640
+ #endif
1641
+
1642
+ rb_define_alias( rb_singleton_class( rb_cStruct), "[]", "new");
1643
+
1644
+ id_delete_at = rb_intern( "delete_at");
1645
+ id_cmp = rb_intern( "<=>");
1646
+ id_eqq = 0;
1647
+ #ifdef FEATURE_ARRAY_SELECT_BANG
1648
+ id_reject_bang = 0;
1649
+ #endif
1650
+ id_chdir = 0;
1651
+ id_path = 0;
1652
+ id_mkdir = 0;
1653
+ id_index = 0;
1654
+
1655
+ Init_supplement_sync();
1656
+ }
1657
+