rumai 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +23 -0
- data/README +1 -0
- data/Rakefile +58 -0
- data/bin/rumai +38 -0
- data/doc/api/classes/IO.html +120 -0
- data/doc/api/classes/Integer.html +142 -0
- data/doc/api/classes/Integer.src/M000002.html +18 -0
- data/doc/api/classes/Object.html +113 -0
- data/doc/api/classes/Rumai.html +530 -0
- data/doc/api/classes/Rumai.src/M000007.html +18 -0
- data/doc/api/classes/Rumai.src/M000008.html +18 -0
- data/doc/api/classes/Rumai.src/M000009.html +18 -0
- data/doc/api/classes/Rumai.src/M000010.html +18 -0
- data/doc/api/classes/Rumai.src/M000011.html +18 -0
- data/doc/api/classes/Rumai.src/M000012.html +18 -0
- data/doc/api/classes/Rumai.src/M000013.html +18 -0
- data/doc/api/classes/Rumai.src/M000014.html +20 -0
- data/doc/api/classes/Rumai.src/M000015.html +18 -0
- data/doc/api/classes/Rumai.src/M000016.html +18 -0
- data/doc/api/classes/Rumai.src/M000017.html +18 -0
- data/doc/api/classes/Rumai.src/M000018.html +18 -0
- data/doc/api/classes/Rumai.src/M000019.html +18 -0
- data/doc/api/classes/Rumai.src/M000020.html +18 -0
- data/doc/api/classes/Rumai.src/M000021.html +18 -0
- data/doc/api/classes/Rumai.src/M000022.html +18 -0
- data/doc/api/classes/Rumai.src/M000023.html +18 -0
- data/doc/api/classes/Rumai.src/M000024.html +18 -0
- data/doc/api/classes/Rumai.src/M000025.html +18 -0
- data/doc/api/classes/Rumai.src/M000026.html +18 -0
- data/doc/api/classes/Rumai/Area.html +461 -0
- data/doc/api/classes/Rumai/Area.src/M000080.html +19 -0
- data/doc/api/classes/Rumai/Area.src/M000081.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000082.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000083.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000084.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000085.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000086.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000087.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000088.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000089.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000090.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000091.html +22 -0
- data/doc/api/classes/Rumai/Area.src/M000093.html +23 -0
- data/doc/api/classes/Rumai/Area.src/M000094.html +28 -0
- data/doc/api/classes/Rumai/Area.src/M000095.html +18 -0
- data/doc/api/classes/Rumai/Area.src/M000096.html +31 -0
- data/doc/api/classes/Rumai/Chain.html +179 -0
- data/doc/api/classes/Rumai/Chain.src/M000077.html +18 -0
- data/doc/api/classes/Rumai/Chain.src/M000078.html +18 -0
- data/doc/api/classes/Rumai/Chain.src/M000079.html +18 -0
- data/doc/api/classes/Rumai/Client.html +463 -0
- data/doc/api/classes/Rumai/Client.src/M000114.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000115.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000116.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000117.html +39 -0
- data/doc/api/classes/Rumai/Client.src/M000118.html +28 -0
- data/doc/api/classes/Rumai/Client.src/M000119.html +19 -0
- data/doc/api/classes/Rumai/Client.src/M000120.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000121.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000122.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000123.html +19 -0
- data/doc/api/classes/Rumai/Client.src/M000124.html +20 -0
- data/doc/api/classes/Rumai/Client.src/M000125.html +20 -0
- data/doc/api/classes/Rumai/Client.src/M000126.html +22 -0
- data/doc/api/classes/Rumai/Client.src/M000127.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000128.html +20 -0
- data/doc/api/classes/Rumai/Client.src/M000129.html +18 -0
- data/doc/api/classes/Rumai/Client.src/M000130.html +22 -0
- data/doc/api/classes/Rumai/ClientContainer.html +180 -0
- data/doc/api/classes/Rumai/ClientContainer.src/M000027.html +18 -0
- data/doc/api/classes/Rumai/ClientContainer.src/M000028.html +18 -0
- data/doc/api/classes/Rumai/ClientContainer.src/M000029.html +18 -0
- data/doc/api/classes/Rumai/ExportInstMethods.html +119 -0
- data/doc/api/classes/Rumai/IXP.html +149 -0
- data/doc/api/classes/Rumai/IXP/Agent.html +447 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000046.html +47 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000047.html +44 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000048.html +39 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000049.html +20 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000050.html +22 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000051.html +20 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000052.html +33 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000053.html +19 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000054.html +18 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000055.html +21 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000056.html +20 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000057.html +20 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000058.html +22 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000059.html +23 -0
- data/doc/api/classes/Rumai/IXP/Agent.src/M000060.html +19 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.html +254 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000062.html +22 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000063.html +21 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000064.html +42 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000065.html +26 -0
- data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000066.html +36 -0
- data/doc/api/classes/Rumai/IXP/Agent/MODES.html +130 -0
- data/doc/api/classes/Rumai/IXP/Agent/MODES.src/M000061.html +22 -0
- data/doc/api/classes/Rumai/IXP/Agent/RangedPool.html +203 -0
- data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000068.html +23 -0
- data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000069.html +32 -0
- data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000070.html +20 -0
- data/doc/api/classes/Rumai/IXP/Error.html +117 -0
- data/doc/api/classes/Rumai/IXP/Fcall.html +261 -0
- data/doc/api/classes/Rumai/IXP/Fcall.src/M000071.html +20 -0
- data/doc/api/classes/Rumai/IXP/Fcall.src/M000072.html +25 -0
- data/doc/api/classes/Rumai/IXP/Fcall.src/M000073.html +18 -0
- data/doc/api/classes/Rumai/IXP/Fcall.src/M000074.html +18 -0
- data/doc/api/classes/Rumai/IXP/Qid.html +184 -0
- data/doc/api/classes/Rumai/IXP/Rattach.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rauth.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rclunk.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rcreate.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rerror.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rflush.html +119 -0
- data/doc/api/classes/Rumai/IXP/Ropen.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rread.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rremove.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rstat.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rversion.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rwalk.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rwrite.html +119 -0
- data/doc/api/classes/Rumai/IXP/Rwstat.html +119 -0
- data/doc/api/classes/Rumai/IXP/Stat.html +248 -0
- data/doc/api/classes/Rumai/IXP/Stat.src/M000076.html +18 -0
- data/doc/api/classes/Rumai/IXP/Stream.html +158 -0
- data/doc/api/classes/Rumai/IXP/Stream.src/M000045.html +19 -0
- data/doc/api/classes/Rumai/IXP/Struct.html +257 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000030.html +19 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000031.html +18 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000032.html +18 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000033.html +18 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000034.html +20 -0
- data/doc/api/classes/Rumai/IXP/Struct.src/M000035.html +57 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.html +304 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000036.html +21 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000037.html +21 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000038.html +20 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000039.html +29 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000040.html +18 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000041.html +18 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.html +152 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.src/M000043.html +24 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.src/M000044.html +25 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field/CounterField.html +137 -0
- data/doc/api/classes/Rumai/IXP/Struct/Field/CounterField.src/M000042.html +18 -0
- data/doc/api/classes/Rumai/IXP/Tattach.html +120 -0
- data/doc/api/classes/Rumai/IXP/Tauth.html +119 -0
- data/doc/api/classes/Rumai/IXP/Tclunk.html +119 -0
- data/doc/api/classes/Rumai/IXP/Tcreate.html +120 -0
- data/doc/api/classes/Rumai/IXP/Terror.html +145 -0
- data/doc/api/classes/Rumai/IXP/Terror.src/M000075.html +18 -0
- data/doc/api/classes/Rumai/IXP/Tflush.html +119 -0
- data/doc/api/classes/Rumai/IXP/Topen.html +193 -0
- data/doc/api/classes/Rumai/IXP/Tread.html +119 -0
- data/doc/api/classes/Rumai/IXP/Tremove.html +119 -0
- data/doc/api/classes/Rumai/IXP/Tstat.html +119 -0
- data/doc/api/classes/Rumai/IXP/Tversion.html +137 -0
- data/doc/api/classes/Rumai/IXP/Twalk.html +120 -0
- data/doc/api/classes/Rumai/IXP/Twrite.html +120 -0
- data/doc/api/classes/Rumai/IXP/Twstat.html +119 -0
- data/doc/api/classes/Rumai/Node.html +461 -0
- data/doc/api/classes/Rumai/Node.src/M000097.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000098.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000099.html +22 -0
- data/doc/api/classes/Rumai/Node.src/M000100.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000101.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000102.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000103.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000104.html +22 -0
- data/doc/api/classes/Rumai/Node.src/M000105.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000106.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000107.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000108.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000109.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000110.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000111.html +18 -0
- data/doc/api/classes/Rumai/Node.src/M000112.html +20 -0
- data/doc/api/classes/Rumai/Node.src/M000113.html +27 -0
- data/doc/api/classes/Rumai/View.html +436 -0
- data/doc/api/classes/Rumai/View.src/M000131.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000132.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000133.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000134.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000135.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000136.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000137.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000138.html +28 -0
- data/doc/api/classes/Rumai/View.src/M000139.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000140.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000141.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000142.html +18 -0
- data/doc/api/classes/Rumai/View.src/M000143.html +29 -0
- data/doc/api/classes/Rumai/View.src/M000144.html +20 -0
- data/doc/api/classes/Rumai/View.src/M000145.html +33 -0
- data/doc/api/classes/Rumai/View.src/M000146.html +50 -0
- data/doc/api/classes/String.html +169 -0
- data/doc/api/classes/String.src/M000005.html +18 -0
- data/doc/api/classes/String.src/M000006.html +19 -0
- data/doc/api/classes/StringIO.html +120 -0
- data/doc/api/classes/Time.html +163 -0
- data/doc/api/classes/Time.src/M000003.html +18 -0
- data/doc/api/classes/Time.src/M000004.html +18 -0
- data/doc/api/created.rid +1 -0
- data/doc/api/files/lib/rumai/fs_rb.html +115 -0
- data/doc/api/files/lib/rumai/ixp/message_rb.html +120 -0
- data/doc/api/files/lib/rumai/ixp/message_spec_rb.html +175 -0
- data/doc/api/files/lib/rumai/ixp/message_spec_rb.src/M000001.html +37 -0
- data/doc/api/files/lib/rumai/ixp/transport_rb.html +115 -0
- data/doc/api/files/lib/rumai/ixp_rb.html +116 -0
- data/doc/api/files/lib/rumai/nfo_rb.html +107 -0
- data/doc/api/files/lib/rumai/wm_rb.html +115 -0
- data/doc/api/files/lib/rumai_rb.html +115 -0
- data/doc/api/fr_class_index.html +82 -0
- data/doc/api/fr_file_index.html +34 -0
- data/doc/api/fr_method_index.html +172 -0
- data/doc/api/index.html +24 -0
- data/doc/api/rdoc-style.css +208 -0
- data/doc/guide.erb +390 -0
- data/doc/guide.html +2035 -0
- data/doc/index.html +2035 -0
- data/lib/rumai.rb +9 -0
- data/lib/rumai/fs.rb +170 -0
- data/lib/rumai/ixp.rb +9 -0
- data/lib/rumai/ixp/message.rb +642 -0
- data/lib/rumai/ixp/message_spec.rb +247 -0
- data/lib/rumai/ixp/transport.rb +382 -0
- data/lib/rumai/nfo.rb +24 -0
- data/lib/rumai/wm.rb +780 -0
- metadata +320 -0
data/lib/rumai.rb
ADDED
data/lib/rumai/fs.rb
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# File system abstractions over the 9P2000 protocol.
|
|
2
|
+
#--
|
|
3
|
+
# Copyright 2006 Suraj N. Kurapati
|
|
4
|
+
# See the file named LICENSE for details.
|
|
5
|
+
|
|
6
|
+
require 'ixp'
|
|
7
|
+
require 'socket'
|
|
8
|
+
|
|
9
|
+
module Rumai
|
|
10
|
+
begin
|
|
11
|
+
addr = ENV['WMII_ADDRESS'].to_s.sub(/.*!/, '')
|
|
12
|
+
sock = UNIXSocket.new(addr)
|
|
13
|
+
|
|
14
|
+
# We use a single, global connection to wmii's IXP server.
|
|
15
|
+
AGENT = IXP::Agent.new(sock)
|
|
16
|
+
|
|
17
|
+
rescue
|
|
18
|
+
$!.message << %{
|
|
19
|
+
Ensure that (1) the WMII_ADDRESS environment variable is set and that (2)
|
|
20
|
+
it correctly specifies the filesystem path of wmii's IXP socket file,
|
|
21
|
+
which is typically located at "/tmp/ns.$USER.:$DISPLAY/wmii".
|
|
22
|
+
}.gsub(/^ +/, '').gsub(/\A|\z/, "\n")
|
|
23
|
+
|
|
24
|
+
raise
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# An entry in the IXP file system.
|
|
28
|
+
class Node
|
|
29
|
+
@@cache = Hash.new {|h,k| h[k] = Node.new(k) }
|
|
30
|
+
|
|
31
|
+
attr_reader :path
|
|
32
|
+
|
|
33
|
+
def initialize aPath
|
|
34
|
+
@path = aPath.to_s.squeeze('/')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Returns file statistics about this node.
|
|
38
|
+
# See Rumai::IXP::Client#stat for details.
|
|
39
|
+
def stat
|
|
40
|
+
AGENT.stat @path
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Tests if this node exists on the IXP server.
|
|
44
|
+
def exist?
|
|
45
|
+
begin
|
|
46
|
+
true if stat
|
|
47
|
+
rescue IXP::Error
|
|
48
|
+
false
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Tests if this node is a directory.
|
|
53
|
+
def directory?
|
|
54
|
+
exist? and stat.directory?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Returns the names of all files in this directory.
|
|
58
|
+
def entries
|
|
59
|
+
AGENT.entries @path rescue []
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Opens this node for I/O access.
|
|
63
|
+
# See Rumai::IXP::Client#open for details.
|
|
64
|
+
def open aMode = 'r', &aBlock
|
|
65
|
+
AGENT.open @path, aMode, &aBlock
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Returns the entire content of this node.
|
|
69
|
+
# See Rumai::IXP::Client#read for details.
|
|
70
|
+
def read
|
|
71
|
+
AGENT.read @path
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Invokes the given block for every line in the content of this node.
|
|
75
|
+
def each_line &aBlock #:yields: line
|
|
76
|
+
open do |file|
|
|
77
|
+
until (chunk = file.read_partial).empty?
|
|
78
|
+
chunk.each_line(&aBlock)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Writes the given content to this node.
|
|
84
|
+
def write aContent
|
|
85
|
+
AGENT.write @path, aContent
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Creates a file corresponding to this node on the IXP server.
|
|
89
|
+
# See Rumai::IXP::Client#create for details.
|
|
90
|
+
def create *aArgs
|
|
91
|
+
AGENT.create @path, *aArgs
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Deletes the file corresponding to this node on the IXP server.
|
|
95
|
+
def remove
|
|
96
|
+
AGENT.remove @path
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Returns the given sub-path as a Node object.
|
|
100
|
+
def [] aSubPath
|
|
101
|
+
@@cache[ File.join(@path, aSubPath.to_s) ]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Returns the parent node of this node.
|
|
105
|
+
def parent
|
|
106
|
+
@@cache[ File.dirname(@path) ]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Returns all child nodes of this node.
|
|
110
|
+
def children
|
|
111
|
+
entries.map! {|c| self[c] }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
include Enumerable
|
|
115
|
+
# Iterates through each child of this directory.
|
|
116
|
+
def each &aBlock
|
|
117
|
+
children.each(&aBlock)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Deletes all child nodes.
|
|
121
|
+
def clear
|
|
122
|
+
children.each do |c|
|
|
123
|
+
c.remove
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Provides access to child nodes through method calls.
|
|
128
|
+
#
|
|
129
|
+
# :call-seq: node.child -> Node
|
|
130
|
+
#
|
|
131
|
+
def method_missing aMeth, *aArgs
|
|
132
|
+
child = self[aMeth]
|
|
133
|
+
|
|
134
|
+
# speed up future accesses
|
|
135
|
+
(class << self; self; end).instance_eval do
|
|
136
|
+
define_method aMeth do
|
|
137
|
+
child
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
child
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Makes instance methods accessible through class
|
|
146
|
+
# methods. This is done to emulate the File class:
|
|
147
|
+
#
|
|
148
|
+
# File.exist? "foo"
|
|
149
|
+
# File.new("foo").exist?
|
|
150
|
+
#
|
|
151
|
+
# Both of the above expressions are equivalent.
|
|
152
|
+
#
|
|
153
|
+
module ExportInstMethods
|
|
154
|
+
def self.extended aTarget #:nodoc:
|
|
155
|
+
aTarget.instance_methods(false).each do |meth|
|
|
156
|
+
(class << aTarget; self; end).instance_eval do
|
|
157
|
+
define_method meth do |path, *args|
|
|
158
|
+
new(path).__send__(meth, *args)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# We use extend() AFTER all methods have been defined in the class so
|
|
166
|
+
# that the Externalize* module can do its magic. If we include()d
|
|
167
|
+
# the module instead before all methods in the class have been
|
|
168
|
+
# defined, then the magic would only apply to SOME of the methods!
|
|
169
|
+
Node.extend ExportInstMethods
|
|
170
|
+
end
|
data/lib/rumai/ixp.rb
ADDED
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
# Primitives for the 9P2000 protocol.
|
|
2
|
+
#
|
|
3
|
+
# See http://cm.bell-labs.com/sys/man/5/INDEX.html
|
|
4
|
+
# See http://swtch.com/plan9port/man/man9/
|
|
5
|
+
#
|
|
6
|
+
#--
|
|
7
|
+
# Copyright 2007 Suraj N. Kurapati
|
|
8
|
+
# See the file named LICENSE for details.
|
|
9
|
+
|
|
10
|
+
module Rumai
|
|
11
|
+
module IXP
|
|
12
|
+
# define constants for easier bit manipulation of 9P2000 field values
|
|
13
|
+
# uchar (1 byte), ushort (2 bytes), uint32 (4 bytes), uint64 (8 bytes)
|
|
14
|
+
4.times do |n|
|
|
15
|
+
bytes = 2 ** n
|
|
16
|
+
bits = 8 * bytes
|
|
17
|
+
limit = 2 ** bits
|
|
18
|
+
mask = limit - 1
|
|
19
|
+
|
|
20
|
+
const_set "BYTE#{bytes}_BITS", bits
|
|
21
|
+
const_set "BYTE#{bytes}_LIMIT", limit
|
|
22
|
+
const_set "BYTE#{bytes}_MASK", mask
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# A 9P2000 byte stream.
|
|
26
|
+
module Stream
|
|
27
|
+
# uchar, ushort, uint32 (all of them little-endian)
|
|
28
|
+
PACKING_FLAGS = { 1 => 'C', 2 => 'v', 4 => 'V' }.freeze
|
|
29
|
+
|
|
30
|
+
# Unpacks the given number of bytes from this 9P2000 byte stream.
|
|
31
|
+
def read_9p aNumBytes
|
|
32
|
+
fmt = PACKING_FLAGS[aNumBytes]
|
|
33
|
+
read(aNumBytes).unpack(fmt)[0]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# A common container for exceptions concerning IXP.
|
|
38
|
+
class Error < StandardError
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# A serializable 9P2000 data structure.
|
|
42
|
+
module Struct
|
|
43
|
+
attr_reader :fields
|
|
44
|
+
|
|
45
|
+
# Allows field values to be initialized via the constructor.
|
|
46
|
+
# aFieldValues:: a mapping from field name to field value
|
|
47
|
+
def initialize aFieldValues = {}
|
|
48
|
+
@fields = self.class.fields
|
|
49
|
+
@values = aFieldValues
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Returns the value of the given field inside this structure.
|
|
53
|
+
def [] aField
|
|
54
|
+
@values[aField.name]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Sets the value of the given field inside this structure.
|
|
58
|
+
def []= aField, aValue
|
|
59
|
+
@values[aField.name] = aValue
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Transforms this object into a string of 9P2000 bytes.
|
|
63
|
+
def to_9p
|
|
64
|
+
fields.inject('') {|s,f| s << f.to_9p(self) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Populates this object with information
|
|
68
|
+
# from the given 9P2000 byte stream.
|
|
69
|
+
def load_9p aStream
|
|
70
|
+
fields.each do |f|
|
|
71
|
+
f.load_9p aStream, self
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Provides a convenient DSL (for defining fields)
|
|
76
|
+
# to all objects which *include* this module.
|
|
77
|
+
def self.included aTarget
|
|
78
|
+
class << aTarget
|
|
79
|
+
# Returns a list of fields which compose this Struct.
|
|
80
|
+
def fields
|
|
81
|
+
@fields ||=
|
|
82
|
+
if superclass.respond_to? :fields
|
|
83
|
+
superclass.fields.dup
|
|
84
|
+
else
|
|
85
|
+
[]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Defines a new field in this Struct.
|
|
90
|
+
# aArgs:: arguments for Field.new()
|
|
91
|
+
def field aName, aFormat = nil, *aArgs
|
|
92
|
+
c = Field.factory(aFormat)
|
|
93
|
+
f = c.new(aName.to_sym, aFormat, *aArgs)
|
|
94
|
+
fields << f # register field as being part of this structure
|
|
95
|
+
|
|
96
|
+
# provide accessor methods to field values
|
|
97
|
+
self.class_eval %{
|
|
98
|
+
def #{f.name}
|
|
99
|
+
@values[#{f.name.inspect}]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def #{f.name}= aValue
|
|
103
|
+
@values[#{f.name.inspect}] = aValue
|
|
104
|
+
end
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return f
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Creates a new instance of this class from the
|
|
111
|
+
# given 9P2000 byte stream and returns the instance.
|
|
112
|
+
def from_9p aStream, aMsgClass = self
|
|
113
|
+
msg = aMsgClass.new
|
|
114
|
+
msg.load_9p(aStream)
|
|
115
|
+
msg
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
# A field inside a Struct.
|
|
123
|
+
#
|
|
124
|
+
# * A field's value is considered to be:
|
|
125
|
+
# * array of format when <code>counter && format.is_a? Class</code>
|
|
126
|
+
# * raw byte string when <code>counter && format.nil?</code>
|
|
127
|
+
#
|
|
128
|
+
# Field values are stored as instance variables inside a structure.
|
|
129
|
+
#
|
|
130
|
+
class Field
|
|
131
|
+
attr_reader :name, :format, :counter, :countee
|
|
132
|
+
|
|
133
|
+
# aName:: unique (among all fields in a struct) name for the field
|
|
134
|
+
# aFormat:: number of bytes, a class, or nil
|
|
135
|
+
# aCounter:: field which counts the length of this field's value
|
|
136
|
+
def initialize aName, aFormat = nil, aCounter = nil
|
|
137
|
+
@name = aName
|
|
138
|
+
@format = aFormat
|
|
139
|
+
@countee = nil
|
|
140
|
+
self.counter = aCounter
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Sets the counter for this field (implying that the
|
|
144
|
+
# length of this field is counted by the given field).
|
|
145
|
+
def counter= aField
|
|
146
|
+
if @counter = aField
|
|
147
|
+
extend CounteeField
|
|
148
|
+
@counter.countee = self
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Sets the countee for this field (implying that
|
|
153
|
+
# this field counts the length of the given field).
|
|
154
|
+
def countee= aField
|
|
155
|
+
if @countee = aField
|
|
156
|
+
extend CounterField
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Returns a Field class that best represents the given format.
|
|
161
|
+
def self.factory aFormat
|
|
162
|
+
if aFormat == String
|
|
163
|
+
StringField
|
|
164
|
+
|
|
165
|
+
elsif aFormat.is_a? Class
|
|
166
|
+
ClassField
|
|
167
|
+
|
|
168
|
+
elsif aFormat == 8
|
|
169
|
+
Integer8Field
|
|
170
|
+
|
|
171
|
+
else
|
|
172
|
+
Field
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Transforms this object into a string of 9P2000 bytes.
|
|
177
|
+
def to_9p aStruct
|
|
178
|
+
value_to_9p aStruct[self]
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Populates this object with information
|
|
182
|
+
# taken from the given 9P2000 byte stream.
|
|
183
|
+
def load_9p aStream, aStruct
|
|
184
|
+
aStruct[self] = value_from_9p aStream
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
private
|
|
188
|
+
|
|
189
|
+
# Converts the given value, according to the format
|
|
190
|
+
# of this field, into a string of 9P2000 bytes.
|
|
191
|
+
def value_to_9p aValue
|
|
192
|
+
aValue.to_i.to_9p @format.to_i
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Parses a value, according to the format of
|
|
196
|
+
# this field, from the given 9P2000 byte stream.
|
|
197
|
+
def value_from_9p aStream
|
|
198
|
+
aStream.read_9p @format.to_i
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Methods for a field that counts the length of another field.
|
|
202
|
+
module CounterField
|
|
203
|
+
def to_9p aStruct
|
|
204
|
+
value_to_9p aStruct[@countee].length
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Methods for a field whose length is counted by another field.
|
|
209
|
+
module CounteeField
|
|
210
|
+
def to_9p aStruct
|
|
211
|
+
value = aStruct[self]
|
|
212
|
+
|
|
213
|
+
if @format
|
|
214
|
+
value.map {|v| value_to_9p v}.join
|
|
215
|
+
else
|
|
216
|
+
value.to_s # raw byte sequence
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def load_9p aStream, aStruct
|
|
221
|
+
count = aStruct[@counter].to_i
|
|
222
|
+
|
|
223
|
+
aStruct[self] =
|
|
224
|
+
if @format
|
|
225
|
+
Array.new(count) { value_from_9p aStream }
|
|
226
|
+
else
|
|
227
|
+
aStream.read(count) # raw byte sequence
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# A field whose value knows how to convert itself to and from 9p.
|
|
234
|
+
class ClassField < Field #:nodoc:
|
|
235
|
+
def value_to_9p aValue
|
|
236
|
+
aValue.to_9p
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def value_from_9p aStream
|
|
240
|
+
@format.from_9p aStream
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# A field whose value is a string.
|
|
245
|
+
class StringField < ClassField #:nodoc:
|
|
246
|
+
def value_to_9p aValue
|
|
247
|
+
aValue.to_s.to_9p
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# A field whose value is a 8-byte integer.
|
|
252
|
+
class Integer8Field < Field #:nodoc:
|
|
253
|
+
def value_to_9p aValue
|
|
254
|
+
v = aValue.to_i
|
|
255
|
+
(BYTE4_MASK & v).to_9p(4) << # lower bytes
|
|
256
|
+
(BYTE4_MASK & (v >> BYTE4_BITS)).to_9p(4) # higher bytes
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def value_from_9p aStream
|
|
260
|
+
aStream.read_9p(4) | (aStream.read_9p(4) << BYTE4_BITS)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Holds information about a file being accessed on a 9P2000 server.
|
|
266
|
+
#
|
|
267
|
+
# See http://cm.bell-labs.com/magic/man2html/5/intro
|
|
268
|
+
class Qid
|
|
269
|
+
include Struct
|
|
270
|
+
|
|
271
|
+
# type[1] version[4] path[8]
|
|
272
|
+
field :type , 1
|
|
273
|
+
field :version , 4
|
|
274
|
+
field :path , 8
|
|
275
|
+
|
|
276
|
+
# from http://swtch.com/usr/local/plan9/include/libc.h
|
|
277
|
+
QTDIR = 0x80 # type bit for directories
|
|
278
|
+
QTAPPEND = 0x40 # type bit for append only files
|
|
279
|
+
QTEXCL = 0x20 # type bit for exclusive use files
|
|
280
|
+
QTMOUNT = 0x10 # type bit for mounted channel
|
|
281
|
+
QTAUTH = 0x08 # type bit for authentication file
|
|
282
|
+
QTTMP = 0x04 # type bit for non-backed-up file
|
|
283
|
+
QTSYMLINK = 0x02 # type bit for symbolic link
|
|
284
|
+
QTFILE = 0x00 # type bits for plain file
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Holds information about a file on a 9P2000 server.
|
|
288
|
+
#
|
|
289
|
+
# See http://cm.bell-labs.com/magic/man2html/5/stat
|
|
290
|
+
class Stat
|
|
291
|
+
include Struct
|
|
292
|
+
|
|
293
|
+
field :size , 2
|
|
294
|
+
field :type , 2
|
|
295
|
+
field :dev , 4
|
|
296
|
+
field :qid , Qid
|
|
297
|
+
field :mode , 4
|
|
298
|
+
field :atime , Time
|
|
299
|
+
field :mtime , Time
|
|
300
|
+
field :length , 8
|
|
301
|
+
field :name , String
|
|
302
|
+
field :uid , String
|
|
303
|
+
field :gid , String
|
|
304
|
+
field :muid , String
|
|
305
|
+
|
|
306
|
+
# from http://swtch.com/usr/local/plan9/include/libc.h
|
|
307
|
+
DMDIR = 0x80000000 # mode bit for directories
|
|
308
|
+
DMAPPEND = 0x40000000 # mode bit for append only files
|
|
309
|
+
DMEXCL = 0x20000000 # mode bit for exclusive use files
|
|
310
|
+
DMMOUNT = 0x10000000 # mode bit for mounted channel
|
|
311
|
+
DMAUTH = 0x08000000 # mode bit for authentication file
|
|
312
|
+
DMTMP = 0x04000000 # mode bit for non-backed-up file
|
|
313
|
+
DMSYMLINK = 0x02000000 # mode bit for symbolic link (Unix, 9P2000.u)
|
|
314
|
+
DMDEVICE = 0x00800000 # mode bit for device file (Unix, 9P2000.u)
|
|
315
|
+
DMNAMEDPIPE = 0x00200000 # mode bit for named pipe (Unix, 9P2000.u)
|
|
316
|
+
DMSOCKET = 0x00100000 # mode bit for socket (Unix, 9P2000.u)
|
|
317
|
+
DMSETUID = 0x00080000 # mode bit for setuid (Unix, 9P2000.u)
|
|
318
|
+
DMSETGID = 0x00040000 # mode bit for setgid (Unix, 9P2000.u)
|
|
319
|
+
DMREAD = 0x4 # mode bit for read permission
|
|
320
|
+
DMWRITE = 0x2 # mode bit for write permission
|
|
321
|
+
DMEXEC = 0x1 # mode bit for execute permission
|
|
322
|
+
|
|
323
|
+
# Tests if this file is a directory.
|
|
324
|
+
def directory?
|
|
325
|
+
mode & DMDIR > 0
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Fcall is the basic unit of communication in the 9P2000 protocol.
|
|
330
|
+
# It is analogous to a "packet" in the Internetwork Protocol (IP).
|
|
331
|
+
#
|
|
332
|
+
# See http://cm.bell-labs.com/magic/man2html/2/fcall
|
|
333
|
+
class Fcall
|
|
334
|
+
include Struct
|
|
335
|
+
|
|
336
|
+
# The first two fields are disabled because they are automatically
|
|
337
|
+
# calculated by the Fcall#to_9p and Fcall::from_9p methods below:
|
|
338
|
+
#
|
|
339
|
+
# field :size , 4 # disabled
|
|
340
|
+
# field :type , 1 # disabled
|
|
341
|
+
#
|
|
342
|
+
field :tag , 2
|
|
343
|
+
|
|
344
|
+
# Transforms this object into a string of 9P2000 bytes.
|
|
345
|
+
def to_9p
|
|
346
|
+
data = type.to_9p(1) << super
|
|
347
|
+
size = (data.length + 4).to_9p(4)
|
|
348
|
+
size << data
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
class << self
|
|
352
|
+
alias __from_9p__ from_9p
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Creates a new instance of this class from the
|
|
356
|
+
# given 9P2000 byte stream and returns the instance.
|
|
357
|
+
def self.from_9p aStream
|
|
358
|
+
size = aStream.read_9p(4)
|
|
359
|
+
type = aStream.read_9p(1)
|
|
360
|
+
|
|
361
|
+
unless fcall = TYPE_TO_CLASS[type]
|
|
362
|
+
raise Error, "illegal fcall type: #{type}"
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
__from_9p__ aStream, fcall
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
NOTAG = BYTE2_MASK # (ushort)
|
|
369
|
+
NOFID = BYTE4_MASK # (uint32)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# size[4] Tversion tag[2] msize[4] version[s]
|
|
373
|
+
class Tversion < Fcall
|
|
374
|
+
field :msize , 4
|
|
375
|
+
field :version , String
|
|
376
|
+
|
|
377
|
+
VERSION = '9P2000'.freeze
|
|
378
|
+
MSIZE = 8192 # magic number defined in Plan9 for [TR]version and [TR]read
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# size[4] Rversion tag[2] msize[4] version[s]
|
|
382
|
+
class Rversion < Fcall
|
|
383
|
+
field :msize , 4
|
|
384
|
+
field :version , String
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# size[4] Tauth tag[2] afid[4] uname[s] aname[s]
|
|
388
|
+
class Tauth < Fcall
|
|
389
|
+
field :afid , 4
|
|
390
|
+
field :uname , String
|
|
391
|
+
field :aname , String
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# size[4] Rauth tag[2] aqid[13]
|
|
395
|
+
class Rauth < Fcall
|
|
396
|
+
field :aqid , Qid
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s]
|
|
400
|
+
class Tattach < Fcall
|
|
401
|
+
field :fid , 4
|
|
402
|
+
field :afid , 4
|
|
403
|
+
field :uname , String
|
|
404
|
+
field :aname , String
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# size[4] Rattach tag[2] qid[13]
|
|
408
|
+
class Rattach < Fcall
|
|
409
|
+
field :qid , Qid
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# illegal
|
|
413
|
+
class Terror < Fcall
|
|
414
|
+
def to_9p
|
|
415
|
+
raise Error, 'the Terror fcall cannot be transmitted'
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
# size[4] Rerror tag[2] ename[s]
|
|
420
|
+
class Rerror < Fcall
|
|
421
|
+
field :ename , String
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
# size[4] Tflush tag[2] oldtag[2]
|
|
425
|
+
class Tflush < Fcall
|
|
426
|
+
field :oldtag , 2
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# size[4] Rflush tag[2]
|
|
430
|
+
class Rflush < Fcall
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
|
|
434
|
+
class Twalk < Fcall
|
|
435
|
+
field :fid , 4
|
|
436
|
+
field :newfid , 4
|
|
437
|
+
c = field :nwname , 2
|
|
438
|
+
field :wname , String , c
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13])
|
|
442
|
+
class Rwalk < Fcall
|
|
443
|
+
c = field :nwqid , 2
|
|
444
|
+
field :wqid , Qid , c
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
# size[4] Topen tag[2] fid[4] mode[1]
|
|
448
|
+
class Topen < Fcall
|
|
449
|
+
field :fid , 4
|
|
450
|
+
field :mode , 1
|
|
451
|
+
|
|
452
|
+
# from http://swtch.com/usr/local/plan9/include/libc.h
|
|
453
|
+
OREAD = 0 # open for read
|
|
454
|
+
OWRITE = 1 # write
|
|
455
|
+
ORDWR = 2 # read and write
|
|
456
|
+
OEXEC = 3 # execute, == read but check execute permission
|
|
457
|
+
OTRUNC = 16 # or'ed in (except for exec), truncate file first
|
|
458
|
+
OCEXEC = 32 # or'ed in, close on exec
|
|
459
|
+
ORCLOSE = 64 # or'ed in, remove on close
|
|
460
|
+
ODIRECT = 128 # or'ed in, direct access
|
|
461
|
+
ONONBLOCK = 256 # or'ed in, non-blocking call
|
|
462
|
+
OEXCL = 0x1000 # or'ed in, exclusive use (create only)
|
|
463
|
+
OLOCK = 0x2000 # or'ed in, lock after opening
|
|
464
|
+
OAPPEND = 0x4000 # or'ed in, append only
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# size[4] Ropen tag[2] qid[13] iounit[4]
|
|
468
|
+
class Ropen < Fcall
|
|
469
|
+
field :qid , Qid
|
|
470
|
+
field :iounit , 4
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
# size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1]
|
|
474
|
+
class Tcreate < Fcall
|
|
475
|
+
field :fid , 4
|
|
476
|
+
field :name , String
|
|
477
|
+
field :perm , 4
|
|
478
|
+
field :mode , 1
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# size[4] Rcreate tag[2] qid[13] iounit[4]
|
|
482
|
+
class Rcreate < Fcall
|
|
483
|
+
field :qid , Qid
|
|
484
|
+
field :iounit , 4
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
# size[4] Tread tag[2] fid[4] offset[8] count[4]
|
|
488
|
+
class Tread < Fcall
|
|
489
|
+
field :fid , 4
|
|
490
|
+
field :offset , 8
|
|
491
|
+
field :count , 4
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
# size[4] Rread tag[2] count[4] data[count]
|
|
495
|
+
class Rread < Fcall
|
|
496
|
+
c = field :count , 4
|
|
497
|
+
field :data , nil , c
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
# size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count]
|
|
501
|
+
class Twrite < Fcall
|
|
502
|
+
field :fid , 4
|
|
503
|
+
field :offset , 8
|
|
504
|
+
c = field :count , 4
|
|
505
|
+
field :data , nil , c
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
# size[4] Rwrite tag[2] count[4]
|
|
509
|
+
class Rwrite < Fcall
|
|
510
|
+
field :count , 4
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
# size[4] Tclunk tag[2] fid[4]
|
|
514
|
+
class Tclunk < Fcall
|
|
515
|
+
field :fid , 4
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
# size[4] Rclunk tag[2]
|
|
519
|
+
class Rclunk < Fcall
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# size[4] Tremove tag[2] fid[4]
|
|
523
|
+
class Tremove < Fcall
|
|
524
|
+
field :fid , 4
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
# size[4] Rremove tag[2]
|
|
528
|
+
class Rremove < Fcall
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
# size[4] Tstat tag[2] fid[4]
|
|
532
|
+
class Tstat < Fcall
|
|
533
|
+
field :fid , 4
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
# size[4] Rstat tag[2] stat[n]
|
|
537
|
+
class Rstat < Fcall
|
|
538
|
+
field :nstat , 2
|
|
539
|
+
field :stat , Stat
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
# size[4] Twstat tag[2] fid[4] stat[n]
|
|
543
|
+
class Twstat < Fcall
|
|
544
|
+
field :fid , 4
|
|
545
|
+
field :nstat , 2
|
|
546
|
+
field :stat , Stat
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
# size[4] Rwstat tag[2]
|
|
550
|
+
class Rwstat < Fcall
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
class Fcall
|
|
554
|
+
CLASS_TO_TYPE = {
|
|
555
|
+
Tversion => 100,
|
|
556
|
+
Rversion => 101,
|
|
557
|
+
Tauth => 102,
|
|
558
|
+
Rauth => 103,
|
|
559
|
+
Tattach => 104,
|
|
560
|
+
Rattach => 105,
|
|
561
|
+
Terror => 106,
|
|
562
|
+
Rerror => 107,
|
|
563
|
+
Tflush => 108,
|
|
564
|
+
Rflush => 109,
|
|
565
|
+
Twalk => 110,
|
|
566
|
+
Rwalk => 111,
|
|
567
|
+
Topen => 112,
|
|
568
|
+
Ropen => 113,
|
|
569
|
+
Tcreate => 114,
|
|
570
|
+
Rcreate => 115,
|
|
571
|
+
Tread => 116,
|
|
572
|
+
Rread => 117,
|
|
573
|
+
Twrite => 118,
|
|
574
|
+
Rwrite => 119,
|
|
575
|
+
Tclunk => 120,
|
|
576
|
+
Rclunk => 121,
|
|
577
|
+
Tremove => 122,
|
|
578
|
+
Rremove => 123,
|
|
579
|
+
Tstat => 124,
|
|
580
|
+
Rstat => 125,
|
|
581
|
+
Twstat => 126,
|
|
582
|
+
Rwstat => 127,
|
|
583
|
+
}.freeze
|
|
584
|
+
|
|
585
|
+
TYPE_TO_CLASS = CLASS_TO_TYPE.invert.freeze
|
|
586
|
+
|
|
587
|
+
# Returns the value of the 'type' field for this fcall.
|
|
588
|
+
def self.type
|
|
589
|
+
CLASS_TO_TYPE[self]
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
# Returns the value of the 'type' field for this fcall.
|
|
593
|
+
def type
|
|
594
|
+
self.class.type
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
class Integer
|
|
601
|
+
# Transforms this object into a string of 9P2000 bytes.
|
|
602
|
+
def to_9p aNumBytes
|
|
603
|
+
[self].pack Rumai::IXP::Stream::PACKING_FLAGS[aNumBytes]
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
# count[2] s[count]
|
|
608
|
+
class String
|
|
609
|
+
# Transforms this object into a string of 9P2000 bytes.
|
|
610
|
+
def to_9p
|
|
611
|
+
length.to_9p(2) << self[0, Rumai::IXP::BYTE2_MASK]
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
# Creates a new instance of this class from the
|
|
615
|
+
# given 9P2000 byte stream and returns the instance.
|
|
616
|
+
def self.from_9p aStream
|
|
617
|
+
count = aStream.read_9p(2)
|
|
618
|
+
aStream.read(count)
|
|
619
|
+
end
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
class Time
|
|
623
|
+
# Transforms this object into a string of 9P2000 bytes.
|
|
624
|
+
def to_9p
|
|
625
|
+
to_i.to_9p(4)
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
# Creates a new instance of this class from the
|
|
629
|
+
# given 9P2000 byte stream and returns the instance.
|
|
630
|
+
def self.from_9p aStream
|
|
631
|
+
at aStream.read_9p(4)
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
class IO
|
|
636
|
+
include Rumai::IXP::Stream
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
require 'stringio'
|
|
640
|
+
class StringIO
|
|
641
|
+
include Rumai::IXP::Stream
|
|
642
|
+
end
|