ffi 1.12.2-java → 1.13.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +3 -3
  4. data/.appveyor.yml +30 -0
  5. data/.github/workflows/ci.yml +64 -0
  6. data/.gitignore +25 -0
  7. data/.gitmodules +4 -0
  8. data/.travis.yml +58 -0
  9. data/.yardopts +5 -0
  10. data/CHANGELOG.md +30 -0
  11. data/Gemfile +17 -0
  12. data/LICENSE.SPECS +22 -0
  13. data/Rakefile +24 -43
  14. data/ffi.gemspec +43 -0
  15. data/lib/ffi.rb +28 -0
  16. data/lib/ffi/autopointer.rb +203 -0
  17. data/lib/ffi/buffer.rb +4 -0
  18. data/lib/ffi/callback.rb +4 -0
  19. data/lib/ffi/data_converter.rb +67 -0
  20. data/lib/ffi/enum.rb +296 -0
  21. data/lib/ffi/errno.rb +43 -0
  22. data/lib/ffi/ffi.rb +46 -0
  23. data/lib/ffi/io.rb +62 -0
  24. data/lib/ffi/library.rb +592 -0
  25. data/lib/ffi/managedstruct.rb +84 -0
  26. data/lib/ffi/memorypointer.rb +1 -0
  27. data/lib/ffi/platform.rb +175 -0
  28. data/lib/ffi/platform/aarch64-freebsd/types.conf +128 -0
  29. data/lib/ffi/platform/aarch64-freebsd12/types.conf +128 -0
  30. data/lib/ffi/platform/aarch64-linux/types.conf +104 -0
  31. data/lib/ffi/platform/arm-freebsd/types.conf +152 -0
  32. data/lib/ffi/platform/arm-freebsd12/types.conf +152 -0
  33. data/lib/ffi/platform/arm-linux/types.conf +132 -0
  34. data/lib/ffi/platform/i386-cygwin/types.conf +3 -0
  35. data/lib/ffi/platform/i386-darwin/types.conf +100 -0
  36. data/lib/ffi/platform/i386-freebsd/types.conf +152 -0
  37. data/lib/ffi/platform/i386-freebsd12/types.conf +152 -0
  38. data/lib/ffi/platform/i386-gnu/types.conf +107 -0
  39. data/lib/ffi/platform/i386-linux/types.conf +103 -0
  40. data/lib/ffi/platform/i386-netbsd/types.conf +126 -0
  41. data/lib/ffi/platform/i386-openbsd/types.conf +128 -0
  42. data/lib/ffi/platform/i386-solaris/types.conf +122 -0
  43. data/lib/ffi/platform/i386-windows/types.conf +52 -0
  44. data/lib/ffi/platform/ia64-linux/types.conf +104 -0
  45. data/lib/ffi/platform/mips-linux/types.conf +102 -0
  46. data/lib/ffi/platform/mips64-linux/types.conf +104 -0
  47. data/lib/ffi/platform/mips64el-linux/types.conf +104 -0
  48. data/lib/ffi/platform/mipsel-linux/types.conf +102 -0
  49. data/lib/ffi/platform/mipsisa32r6-linux/types.conf +102 -0
  50. data/lib/ffi/platform/mipsisa32r6el-linux/types.conf +102 -0
  51. data/lib/ffi/platform/mipsisa64r6-linux/types.conf +104 -0
  52. data/lib/ffi/platform/mipsisa64r6el-linux/types.conf +104 -0
  53. data/lib/ffi/platform/powerpc-aix/types.conf +180 -0
  54. data/lib/ffi/platform/powerpc-darwin/types.conf +100 -0
  55. data/lib/ffi/platform/powerpc-linux/types.conf +130 -0
  56. data/lib/ffi/platform/powerpc-openbsd/types.conf +156 -0
  57. data/lib/ffi/platform/powerpc64-linux/types.conf +104 -0
  58. data/lib/ffi/platform/s390-linux/types.conf +102 -0
  59. data/lib/ffi/platform/s390x-linux/types.conf +102 -0
  60. data/lib/ffi/platform/sparc-linux/types.conf +102 -0
  61. data/lib/ffi/platform/sparc-solaris/types.conf +128 -0
  62. data/lib/ffi/platform/sparc64-linux/types.conf +102 -0
  63. data/lib/ffi/platform/sparcv9-openbsd/types.conf +156 -0
  64. data/lib/ffi/platform/sparcv9-solaris/types.conf +128 -0
  65. data/lib/ffi/platform/x86_64-cygwin/types.conf +3 -0
  66. data/lib/ffi/platform/x86_64-darwin/types.conf +130 -0
  67. data/lib/ffi/platform/x86_64-dragonflybsd/types.conf +148 -0
  68. data/lib/ffi/platform/x86_64-freebsd/types.conf +128 -0
  69. data/lib/ffi/platform/x86_64-freebsd12/types.conf +158 -0
  70. data/lib/ffi/platform/x86_64-linux/types.conf +132 -0
  71. data/lib/ffi/platform/x86_64-netbsd/types.conf +128 -0
  72. data/lib/ffi/platform/x86_64-openbsd/types.conf +134 -0
  73. data/lib/ffi/platform/x86_64-solaris/types.conf +122 -0
  74. data/lib/ffi/platform/x86_64-windows/types.conf +52 -0
  75. data/lib/ffi/pointer.rb +167 -0
  76. data/lib/ffi/struct.rb +316 -0
  77. data/lib/ffi/struct_by_reference.rb +72 -0
  78. data/lib/ffi/struct_layout.rb +96 -0
  79. data/lib/ffi/struct_layout_builder.rb +227 -0
  80. data/lib/ffi/tools/const_generator.rb +230 -0
  81. data/lib/ffi/tools/generator.rb +105 -0
  82. data/lib/ffi/tools/generator_task.rb +32 -0
  83. data/lib/ffi/tools/struct_generator.rb +194 -0
  84. data/lib/ffi/tools/types_generator.rb +137 -0
  85. data/lib/ffi/types.rb +194 -0
  86. data/lib/ffi/union.rb +43 -0
  87. data/lib/ffi/variadic.rb +78 -0
  88. data/lib/ffi/version.rb +3 -0
  89. data/samples/getlogin.rb +8 -0
  90. data/samples/getpid.rb +8 -0
  91. data/samples/gettimeofday.rb +18 -0
  92. data/samples/hello.rb +8 -0
  93. data/samples/inotify.rb +60 -0
  94. data/samples/pty.rb +75 -0
  95. data/samples/qsort.rb +20 -0
  96. metadata +192 -24
  97. metadata.gz.sig +0 -0
@@ -0,0 +1,194 @@
1
+ #
2
+ # Copyright (C) 2008-2010 Wayne Meissner
3
+ # Copyright (c) 2007, 2008 Evan Phoenix
4
+ #
5
+ # This file is part of ruby-ffi.
6
+ #
7
+ # All rights reserved.
8
+ #
9
+ # Redistribution and use in source and binary forms, with or without
10
+ # modification, are permitted provided that the following conditions are met:
11
+ #
12
+ # * Redistributions of source code must retain the above copyright notice, this
13
+ # list of conditions and the following disclaimer.
14
+ # * Redistributions in binary form must reproduce the above copyright notice
15
+ # this list of conditions and the following disclaimer in the documentation
16
+ # and/or other materials provided with the distribution.
17
+ # * Neither the name of the Ruby FFI project nor the names of its contributors
18
+ # may be used to endorse or promote products derived from this software
19
+ # without specific prior written permission.
20
+ #
21
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
25
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ #
32
+
33
+ # see {file:README}
34
+ module FFI
35
+
36
+ # @param [Type, DataConverter, Symbol] old type definition used by {FFI.find_type}
37
+ # @param [Symbol] add new type definition's name to add
38
+ # @return [Type]
39
+ # Add a definition type to type definitions.
40
+ def self.typedef(old, add)
41
+ TypeDefs[add] = self.find_type(old)
42
+ end
43
+
44
+ # (see FFI.typedef)
45
+ def self.add_typedef(old, add)
46
+ typedef old, add
47
+ end
48
+
49
+
50
+ # @param [Type, DataConverter, Symbol] name
51
+ # @param [Hash] type_map if nil, {FFI::TypeDefs} is used
52
+ # @return [Type]
53
+ # Find a type in +type_map+ ({FFI::TypeDefs}, by default) from
54
+ # a type objet, a type name (symbol). If +name+ is a {DataConverter},
55
+ # a new {Type::Mapped} is created.
56
+ def self.find_type(name, type_map = nil)
57
+ if name.is_a?(Type)
58
+ name
59
+
60
+ elsif type_map && type_map.has_key?(name)
61
+ type_map[name]
62
+
63
+ elsif TypeDefs.has_key?(name)
64
+ TypeDefs[name]
65
+
66
+ elsif name.is_a?(DataConverter)
67
+ (type_map || TypeDefs)[name] = Type::Mapped.new(name)
68
+ else
69
+ raise TypeError, "unable to resolve type '#{name}'"
70
+ end
71
+ end
72
+
73
+ # List of type definitions
74
+ TypeDefs.merge!({
75
+ # The C void type; only useful for function return types
76
+ :void => Type::VOID,
77
+
78
+ # C boolean type
79
+ :bool => Type::BOOL,
80
+
81
+ # C nul-terminated string
82
+ :string => Type::STRING,
83
+
84
+ # C signed char
85
+ :char => Type::CHAR,
86
+ # C unsigned char
87
+ :uchar => Type::UCHAR,
88
+
89
+ # C signed short
90
+ :short => Type::SHORT,
91
+ # C unsigned short
92
+ :ushort => Type::USHORT,
93
+
94
+ # C signed int
95
+ :int => Type::INT,
96
+ # C unsigned int
97
+ :uint => Type::UINT,
98
+
99
+ # C signed long
100
+ :long => Type::LONG,
101
+
102
+ # C unsigned long
103
+ :ulong => Type::ULONG,
104
+
105
+ # C signed long long integer
106
+ :long_long => Type::LONG_LONG,
107
+
108
+ # C unsigned long long integer
109
+ :ulong_long => Type::ULONG_LONG,
110
+
111
+ # C single precision float
112
+ :float => Type::FLOAT,
113
+
114
+ # C double precision float
115
+ :double => Type::DOUBLE,
116
+
117
+ # C long double
118
+ :long_double => Type::LONGDOUBLE,
119
+
120
+ # Native memory address
121
+ :pointer => Type::POINTER,
122
+
123
+ # 8 bit signed integer
124
+ :int8 => Type::INT8,
125
+ # 8 bit unsigned integer
126
+ :uint8 => Type::UINT8,
127
+
128
+ # 16 bit signed integer
129
+ :int16 => Type::INT16,
130
+ # 16 bit unsigned integer
131
+ :uint16 => Type::UINT16,
132
+
133
+ # 32 bit signed integer
134
+ :int32 => Type::INT32,
135
+ # 32 bit unsigned integer
136
+ :uint32 => Type::UINT32,
137
+
138
+ # 64 bit signed integer
139
+ :int64 => Type::INT64,
140
+ # 64 bit unsigned integer
141
+ :uint64 => Type::UINT64,
142
+
143
+ :buffer_in => Type::BUFFER_IN,
144
+ :buffer_out => Type::BUFFER_OUT,
145
+ :buffer_inout => Type::BUFFER_INOUT,
146
+
147
+ # Used in function prototypes to indicate the arguments are variadic
148
+ :varargs => Type::VARARGS,
149
+ })
150
+
151
+ # This will convert a pointer to a Ruby string (just like `:string`), but
152
+ # also allow to work with the pointer itself. This is useful when you want
153
+ # a Ruby string already containing a copy of the data, but also the pointer
154
+ # to the data for you to do something with it, like freeing it, in case the
155
+ # library handed the memory off to the caller (Ruby-FFI).
156
+ #
157
+ # It's {typedef}'d as +:strptr+.
158
+ class StrPtrConverter
159
+ extend DataConverter
160
+ native_type Type::POINTER
161
+
162
+ # @param [Pointer] val
163
+ # @param ctx not used
164
+ # @return [Array(String, Pointer)]
165
+ # Returns a [ String, Pointer ] tuple so the C memory for the string can be freed
166
+ def self.from_native(val, ctx)
167
+ [ val.null? ? nil : val.get_string(0), val ]
168
+ end
169
+ end
170
+
171
+ typedef(StrPtrConverter, :strptr)
172
+
173
+ # @param type +type+ is an instance of class accepted by {FFI.find_type}
174
+ # @return [Numeric]
175
+ # Get +type+ size, in bytes.
176
+ def self.type_size(type)
177
+ find_type(type).size
178
+ end
179
+
180
+ # Load all the platform dependent types
181
+ begin
182
+ File.open(File.join(Platform::CONF_DIR, 'types.conf'), "r") do |f|
183
+ prefix = "rbx.platform.typedef."
184
+ f.each_line { |line|
185
+ if line.index(prefix) == 0
186
+ new_type, orig_type = line.chomp.slice(prefix.length..-1).split(/\s*=\s*/)
187
+ typedef(orig_type.to_sym, new_type.to_sym)
188
+ end
189
+ }
190
+ end
191
+ typedef :pointer, :caddr_t
192
+ rescue Errno::ENOENT
193
+ end
194
+ end
@@ -0,0 +1,43 @@
1
+ #
2
+ # Copyright (C) 2009 Andrea Fazzi <andrea.fazzi@alcacoop.it>
3
+ #
4
+ # This file is part of ruby-ffi.
5
+ #
6
+ # All rights reserved.
7
+ #
8
+ # Redistribution and use in source and binary forms, with or without
9
+ # modification, are permitted provided that the following conditions are met:
10
+ #
11
+ # * Redistributions of source code must retain the above copyright notice, this
12
+ # list of conditions and the following disclaimer.
13
+ # * Redistributions in binary form must reproduce the above copyright notice
14
+ # this list of conditions and the following disclaimer in the documentation
15
+ # and/or other materials provided with the distribution.
16
+ # * Neither the name of the Ruby FFI project nor the names of its contributors
17
+ # may be used to endorse or promote products derived from this software
18
+ # without specific prior written permission.
19
+ #
20
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
24
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ #
31
+
32
+ require 'ffi/struct'
33
+
34
+ module FFI
35
+
36
+ class Union < FFI::Struct
37
+ def self.builder
38
+ b = StructLayoutBuilder.new
39
+ b.union = true
40
+ b
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,78 @@
1
+ #
2
+ # Copyright (C) 2008, 2009 Wayne Meissner
3
+ # Copyright (C) 2009 Luc Heinrich
4
+ #
5
+ # This file is part of ruby-ffi.
6
+ #
7
+ # All rights reserved.
8
+ #
9
+ # Redistribution and use in source and binary forms, with or without
10
+ # modification, are permitted provided that the following conditions are met:
11
+ #
12
+ # * Redistributions of source code must retain the above copyright notice, this
13
+ # list of conditions and the following disclaimer.
14
+ # * Redistributions in binary form must reproduce the above copyright notice
15
+ # this list of conditions and the following disclaimer in the documentation
16
+ # and/or other materials provided with the distribution.
17
+ # * Neither the name of the Ruby FFI project nor the names of its contributors
18
+ # may be used to endorse or promote products derived from this software
19
+ # without specific prior written permission.
20
+ #
21
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
25
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ #
32
+
33
+ module FFI
34
+ class VariadicInvoker
35
+ def init(arg_types, type_map)
36
+ @fixed = Array.new
37
+ @type_map = type_map
38
+ arg_types.each_with_index do |type, i|
39
+ @fixed << type unless type == Type::VARARGS
40
+ end
41
+ end
42
+
43
+
44
+ def call(*args, &block)
45
+ param_types = Array.new(@fixed)
46
+ param_values = Array.new
47
+ @fixed.each_with_index do |t, i|
48
+ param_values << args[i]
49
+ end
50
+ i = @fixed.length
51
+ while i < args.length
52
+ param_types << FFI.find_type(args[i], @type_map)
53
+ param_values << args[i + 1]
54
+ i += 2
55
+ end
56
+ invoke(param_types, param_values, &block)
57
+ end
58
+
59
+ #
60
+ # Attach the invoker to module +mod+ as +mname+
61
+ #
62
+ def attach(mod, mname)
63
+ invoker = self
64
+ params = "*args"
65
+ call = "call"
66
+ mod.module_eval <<-code
67
+ @@#{mname} = invoker
68
+ def self.#{mname}(#{params})
69
+ @@#{mname}.#{call}(#{params})
70
+ end
71
+ def #{mname}(#{params})
72
+ @@#{mname}.#{call}(#{params})
73
+ end
74
+ code
75
+ invoker
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,3 @@
1
+ module FFI
2
+ VERSION = '1.13.0'
3
+ end
@@ -0,0 +1,8 @@
1
+ require 'ffi'
2
+
3
+ module Foo
4
+ extend FFI::Library
5
+ ffi_lib FFI::Library::LIBC
6
+ attach_function :getlogin, [ ], :string
7
+ end
8
+ puts "getlogin=#{Foo.getlogin}"
@@ -0,0 +1,8 @@
1
+ require 'ffi'
2
+
3
+ module Foo
4
+ extend FFI::Library
5
+ ffi_lib FFI::Library::LIBC
6
+ attach_function :getpid, [ ], :int
7
+ end
8
+ puts "My pid=#{Foo.getpid}"
@@ -0,0 +1,18 @@
1
+ require 'ffi'
2
+ require 'rbconfig'
3
+
4
+ class Timeval < FFI::Struct
5
+ layout tv_sec: :ulong, tv_usec: :ulong
6
+ end
7
+ module LibC
8
+ extend FFI::Library
9
+ if FFI::Platform.windows?
10
+ ffi_lib RbConfig::CONFIG["LIBRUBY_SO"]
11
+ else
12
+ ffi_lib FFI::Library::LIBC
13
+ end
14
+ attach_function :gettimeofday, [ :pointer, :pointer ], :int
15
+ end
16
+ t = Timeval.new
17
+ LibC.gettimeofday(t.pointer, nil)
18
+ puts "t.tv_sec=#{t[:tv_sec]} t.tv_usec=#{t[:tv_usec]}"
@@ -0,0 +1,8 @@
1
+ require 'ffi'
2
+
3
+ module Foo
4
+ extend FFI::Library
5
+ ffi_lib FFI::Library::LIBC
6
+ attach_function("cputs", "puts", [ :string ], :int)
7
+ end
8
+ Foo.cputs("Hello, World via libc puts using FFI on MRI ruby")
@@ -0,0 +1,60 @@
1
+ require 'ffi'
2
+
3
+ module Inotify
4
+ extend FFI::Library
5
+ ffi_lib FFI::Library::LIBC
6
+ class Event < FFI::Struct
7
+ layout \
8
+ :wd, :int,
9
+ :mask, :uint,
10
+ :cookie, :uint,
11
+ :len, :uint
12
+ end
13
+ attach_function :init, :inotify_init, [ ], :int
14
+ attach_function :add_watch, :inotify_add_watch, [ :int, :string, :uint ], :int
15
+ attach_function :rm_watch, :inotify_rm_watch, [ :int, :uint ], :int
16
+ attach_function :read, [ :int, :buffer_out, :uint ], :int
17
+ IN_ACCESS=0x00000001
18
+ IN_MODIFY=0x00000002
19
+ IN_ATTRIB=0x00000004
20
+ IN_CLOSE_WRITE=0x00000008
21
+ IN_CLOSE_NOWRITE=0x00000010
22
+ IN_CLOSE=(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
23
+ IN_OPEN=0x00000020
24
+ IN_MOVED_FROM=0x00000040
25
+ IN_MOVED_TO=0x00000080
26
+ IN_MOVE= (IN_MOVED_FROM | IN_MOVED_TO)
27
+ IN_CREATE=0x00000100
28
+ IN_DELETE=0x00000200
29
+ IN_DELETE_SELF=0x00000400
30
+ IN_MOVE_SELF=0x00000800
31
+ # Events sent by the kernel.
32
+ IN_UNMOUNT=0x00002000
33
+ IN_Q_OVERFLOW=0x00004000
34
+ IN_IGNORED=0x00008000
35
+ IN_ONLYDIR=0x01000000
36
+ IN_DONT_FOLLOW=0x02000000
37
+ IN_MASK_ADD=0x20000000
38
+ IN_ISDIR=0x40000000
39
+ IN_ONESHOT=0x80000000
40
+ IN_ALL_EVENTS=(IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE \
41
+ | IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM \
42
+ | IN_MOVED_TO | IN_CREATE | IN_DELETE \
43
+ | IN_DELETE_SELF | IN_MOVE_SELF)
44
+
45
+ end
46
+ if $0 == __FILE__
47
+ fd = Inotify.init
48
+ puts "fd=#{fd}"
49
+ wd = Inotify.add_watch(fd, "/tmp/", Inotify::IN_ALL_EVENTS)
50
+ fp = FFI::IO.for_fd(fd)
51
+ puts "wfp=#{fp}"
52
+ while true
53
+ buf = FFI::Buffer.alloc_out(Inotify::Event.size + 4096, 1, false)
54
+ ev = Inotify::Event.new buf
55
+ ready = IO.select([ fp ], nil, nil, nil)
56
+ n = Inotify.read(fd, buf, buf.total)
57
+ puts "Read #{n} bytes from inotify fd"
58
+ puts "event.wd=#{ev[:wd]} mask=#{ev[:mask]} len=#{ev[:len]} name=#{ev[:len] > 0 ? buf.get_string(16) : 'unknown'}"
59
+ end
60
+ end
@@ -0,0 +1,75 @@
1
+ require 'ffi'
2
+
3
+ module PTY
4
+ private
5
+ module LibC
6
+ extend FFI::Library
7
+ ffi_lib FFI::Library::LIBC
8
+ attach_function :forkpty, [ :buffer_out, :buffer_out, :buffer_in, :buffer_in ], :int
9
+ attach_function :openpty, [ :buffer_out, :buffer_out, :buffer_out, :buffer_in, :buffer_in ], :int
10
+ attach_function :login_tty, [ :int ], :int
11
+ attach_function :close, [ :int ], :int
12
+ attach_function :strerror, [ :int ], :string
13
+ attach_function :fork, [], :int
14
+ attach_function :execv, [ :string, :buffer_in ], :int
15
+ attach_function :execvp, [ :string, :buffer_in ], :int
16
+ attach_function :dup2, [ :int, :int ], :int
17
+ attach_function :dup, [ :int ], :int
18
+ end
19
+ Buffer = FFI::Buffer
20
+ def self.build_args(args)
21
+ cmd = args.shift
22
+ cmd_args = args.map do |arg|
23
+ MemoryPointer.from_string(arg)
24
+ end
25
+ exec_args = MemoryPointer.new(:pointer, 1 + cmd_args.length + 1)
26
+ exec_cmd = MemoryPointer.from_string(cmd)
27
+ exec_args[0].put_pointer(0, exec_cmd)
28
+ cmd_args.each_with_index do |arg, i|
29
+ exec_args[i + 1].put_pointer(0, arg)
30
+ end
31
+ [ cmd, exec_args ]
32
+ end
33
+ public
34
+ def self.getpty(*args)
35
+ mfdp = Buffer.new :int
36
+ name = Buffer.new 1024
37
+ #
38
+ # All the execv setup is done in the parent, since doing anything other than
39
+ # execv in the child after fork is really flakey
40
+ #
41
+ exec_cmd, exec_args = build_args(args)
42
+ pid = LibC.forkpty(mfdp, name, nil, nil)
43
+ raise "forkpty failed: #{LibC.strerror(FFI.errno)}" if pid < 0
44
+ if pid == 0
45
+ LibC.execvp(exec_cmd, exec_args)
46
+ exit 1
47
+ end
48
+ masterfd = mfdp.get_int(0)
49
+ rfp = FFI::IO.for_fd(masterfd, "r")
50
+ wfp = FFI::IO.for_fd(LibC.dup(masterfd), "w")
51
+ if block_given?
52
+ yield rfp, wfp, pid
53
+ rfp.close unless rfp.closed?
54
+ wfp.close unless wfp.closed?
55
+ else
56
+ [ rfp, wfp, pid ]
57
+ end
58
+ end
59
+ def self.spawn(*args, &block)
60
+ self.getpty("/bin/sh", "-c", args[0], &block)
61
+ end
62
+ end
63
+ module LibC
64
+ extend FFI::Library
65
+ attach_function :close, [ :int ], :int
66
+ attach_function :write, [ :int, :buffer_in, :ulong ], :long
67
+ attach_function :read, [ :int, :buffer_out, :ulong ], :long
68
+ end
69
+ PTY.getpty("/bin/ls", "-alR", "/") { |rfd, wfd, pid|
70
+ #PTY.spawn("ls -laR /") { |rfd, wfd, pid|
71
+ puts "child pid=#{pid}"
72
+ while !rfd.eof? && (buf = rfd.gets)
73
+ puts "child: '#{buf.strip}'"
74
+ end
75
+ }