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.
Files changed (230) hide show
  1. data/LICENSE +23 -0
  2. data/README +1 -0
  3. data/Rakefile +58 -0
  4. data/bin/rumai +38 -0
  5. data/doc/api/classes/IO.html +120 -0
  6. data/doc/api/classes/Integer.html +142 -0
  7. data/doc/api/classes/Integer.src/M000002.html +18 -0
  8. data/doc/api/classes/Object.html +113 -0
  9. data/doc/api/classes/Rumai.html +530 -0
  10. data/doc/api/classes/Rumai.src/M000007.html +18 -0
  11. data/doc/api/classes/Rumai.src/M000008.html +18 -0
  12. data/doc/api/classes/Rumai.src/M000009.html +18 -0
  13. data/doc/api/classes/Rumai.src/M000010.html +18 -0
  14. data/doc/api/classes/Rumai.src/M000011.html +18 -0
  15. data/doc/api/classes/Rumai.src/M000012.html +18 -0
  16. data/doc/api/classes/Rumai.src/M000013.html +18 -0
  17. data/doc/api/classes/Rumai.src/M000014.html +20 -0
  18. data/doc/api/classes/Rumai.src/M000015.html +18 -0
  19. data/doc/api/classes/Rumai.src/M000016.html +18 -0
  20. data/doc/api/classes/Rumai.src/M000017.html +18 -0
  21. data/doc/api/classes/Rumai.src/M000018.html +18 -0
  22. data/doc/api/classes/Rumai.src/M000019.html +18 -0
  23. data/doc/api/classes/Rumai.src/M000020.html +18 -0
  24. data/doc/api/classes/Rumai.src/M000021.html +18 -0
  25. data/doc/api/classes/Rumai.src/M000022.html +18 -0
  26. data/doc/api/classes/Rumai.src/M000023.html +18 -0
  27. data/doc/api/classes/Rumai.src/M000024.html +18 -0
  28. data/doc/api/classes/Rumai.src/M000025.html +18 -0
  29. data/doc/api/classes/Rumai.src/M000026.html +18 -0
  30. data/doc/api/classes/Rumai/Area.html +461 -0
  31. data/doc/api/classes/Rumai/Area.src/M000080.html +19 -0
  32. data/doc/api/classes/Rumai/Area.src/M000081.html +18 -0
  33. data/doc/api/classes/Rumai/Area.src/M000082.html +18 -0
  34. data/doc/api/classes/Rumai/Area.src/M000083.html +18 -0
  35. data/doc/api/classes/Rumai/Area.src/M000084.html +18 -0
  36. data/doc/api/classes/Rumai/Area.src/M000085.html +18 -0
  37. data/doc/api/classes/Rumai/Area.src/M000086.html +18 -0
  38. data/doc/api/classes/Rumai/Area.src/M000087.html +18 -0
  39. data/doc/api/classes/Rumai/Area.src/M000088.html +18 -0
  40. data/doc/api/classes/Rumai/Area.src/M000089.html +18 -0
  41. data/doc/api/classes/Rumai/Area.src/M000090.html +18 -0
  42. data/doc/api/classes/Rumai/Area.src/M000091.html +22 -0
  43. data/doc/api/classes/Rumai/Area.src/M000093.html +23 -0
  44. data/doc/api/classes/Rumai/Area.src/M000094.html +28 -0
  45. data/doc/api/classes/Rumai/Area.src/M000095.html +18 -0
  46. data/doc/api/classes/Rumai/Area.src/M000096.html +31 -0
  47. data/doc/api/classes/Rumai/Chain.html +179 -0
  48. data/doc/api/classes/Rumai/Chain.src/M000077.html +18 -0
  49. data/doc/api/classes/Rumai/Chain.src/M000078.html +18 -0
  50. data/doc/api/classes/Rumai/Chain.src/M000079.html +18 -0
  51. data/doc/api/classes/Rumai/Client.html +463 -0
  52. data/doc/api/classes/Rumai/Client.src/M000114.html +18 -0
  53. data/doc/api/classes/Rumai/Client.src/M000115.html +18 -0
  54. data/doc/api/classes/Rumai/Client.src/M000116.html +18 -0
  55. data/doc/api/classes/Rumai/Client.src/M000117.html +39 -0
  56. data/doc/api/classes/Rumai/Client.src/M000118.html +28 -0
  57. data/doc/api/classes/Rumai/Client.src/M000119.html +19 -0
  58. data/doc/api/classes/Rumai/Client.src/M000120.html +18 -0
  59. data/doc/api/classes/Rumai/Client.src/M000121.html +18 -0
  60. data/doc/api/classes/Rumai/Client.src/M000122.html +18 -0
  61. data/doc/api/classes/Rumai/Client.src/M000123.html +19 -0
  62. data/doc/api/classes/Rumai/Client.src/M000124.html +20 -0
  63. data/doc/api/classes/Rumai/Client.src/M000125.html +20 -0
  64. data/doc/api/classes/Rumai/Client.src/M000126.html +22 -0
  65. data/doc/api/classes/Rumai/Client.src/M000127.html +18 -0
  66. data/doc/api/classes/Rumai/Client.src/M000128.html +20 -0
  67. data/doc/api/classes/Rumai/Client.src/M000129.html +18 -0
  68. data/doc/api/classes/Rumai/Client.src/M000130.html +22 -0
  69. data/doc/api/classes/Rumai/ClientContainer.html +180 -0
  70. data/doc/api/classes/Rumai/ClientContainer.src/M000027.html +18 -0
  71. data/doc/api/classes/Rumai/ClientContainer.src/M000028.html +18 -0
  72. data/doc/api/classes/Rumai/ClientContainer.src/M000029.html +18 -0
  73. data/doc/api/classes/Rumai/ExportInstMethods.html +119 -0
  74. data/doc/api/classes/Rumai/IXP.html +149 -0
  75. data/doc/api/classes/Rumai/IXP/Agent.html +447 -0
  76. data/doc/api/classes/Rumai/IXP/Agent.src/M000046.html +47 -0
  77. data/doc/api/classes/Rumai/IXP/Agent.src/M000047.html +44 -0
  78. data/doc/api/classes/Rumai/IXP/Agent.src/M000048.html +39 -0
  79. data/doc/api/classes/Rumai/IXP/Agent.src/M000049.html +20 -0
  80. data/doc/api/classes/Rumai/IXP/Agent.src/M000050.html +22 -0
  81. data/doc/api/classes/Rumai/IXP/Agent.src/M000051.html +20 -0
  82. data/doc/api/classes/Rumai/IXP/Agent.src/M000052.html +33 -0
  83. data/doc/api/classes/Rumai/IXP/Agent.src/M000053.html +19 -0
  84. data/doc/api/classes/Rumai/IXP/Agent.src/M000054.html +18 -0
  85. data/doc/api/classes/Rumai/IXP/Agent.src/M000055.html +21 -0
  86. data/doc/api/classes/Rumai/IXP/Agent.src/M000056.html +20 -0
  87. data/doc/api/classes/Rumai/IXP/Agent.src/M000057.html +20 -0
  88. data/doc/api/classes/Rumai/IXP/Agent.src/M000058.html +22 -0
  89. data/doc/api/classes/Rumai/IXP/Agent.src/M000059.html +23 -0
  90. data/doc/api/classes/Rumai/IXP/Agent.src/M000060.html +19 -0
  91. data/doc/api/classes/Rumai/IXP/Agent/FidStream.html +254 -0
  92. data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000062.html +22 -0
  93. data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000063.html +21 -0
  94. data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000064.html +42 -0
  95. data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000065.html +26 -0
  96. data/doc/api/classes/Rumai/IXP/Agent/FidStream.src/M000066.html +36 -0
  97. data/doc/api/classes/Rumai/IXP/Agent/MODES.html +130 -0
  98. data/doc/api/classes/Rumai/IXP/Agent/MODES.src/M000061.html +22 -0
  99. data/doc/api/classes/Rumai/IXP/Agent/RangedPool.html +203 -0
  100. data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000068.html +23 -0
  101. data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000069.html +32 -0
  102. data/doc/api/classes/Rumai/IXP/Agent/RangedPool.src/M000070.html +20 -0
  103. data/doc/api/classes/Rumai/IXP/Error.html +117 -0
  104. data/doc/api/classes/Rumai/IXP/Fcall.html +261 -0
  105. data/doc/api/classes/Rumai/IXP/Fcall.src/M000071.html +20 -0
  106. data/doc/api/classes/Rumai/IXP/Fcall.src/M000072.html +25 -0
  107. data/doc/api/classes/Rumai/IXP/Fcall.src/M000073.html +18 -0
  108. data/doc/api/classes/Rumai/IXP/Fcall.src/M000074.html +18 -0
  109. data/doc/api/classes/Rumai/IXP/Qid.html +184 -0
  110. data/doc/api/classes/Rumai/IXP/Rattach.html +119 -0
  111. data/doc/api/classes/Rumai/IXP/Rauth.html +119 -0
  112. data/doc/api/classes/Rumai/IXP/Rclunk.html +119 -0
  113. data/doc/api/classes/Rumai/IXP/Rcreate.html +119 -0
  114. data/doc/api/classes/Rumai/IXP/Rerror.html +119 -0
  115. data/doc/api/classes/Rumai/IXP/Rflush.html +119 -0
  116. data/doc/api/classes/Rumai/IXP/Ropen.html +119 -0
  117. data/doc/api/classes/Rumai/IXP/Rread.html +119 -0
  118. data/doc/api/classes/Rumai/IXP/Rremove.html +119 -0
  119. data/doc/api/classes/Rumai/IXP/Rstat.html +119 -0
  120. data/doc/api/classes/Rumai/IXP/Rversion.html +119 -0
  121. data/doc/api/classes/Rumai/IXP/Rwalk.html +119 -0
  122. data/doc/api/classes/Rumai/IXP/Rwrite.html +119 -0
  123. data/doc/api/classes/Rumai/IXP/Rwstat.html +119 -0
  124. data/doc/api/classes/Rumai/IXP/Stat.html +248 -0
  125. data/doc/api/classes/Rumai/IXP/Stat.src/M000076.html +18 -0
  126. data/doc/api/classes/Rumai/IXP/Stream.html +158 -0
  127. data/doc/api/classes/Rumai/IXP/Stream.src/M000045.html +19 -0
  128. data/doc/api/classes/Rumai/IXP/Struct.html +257 -0
  129. data/doc/api/classes/Rumai/IXP/Struct.src/M000030.html +19 -0
  130. data/doc/api/classes/Rumai/IXP/Struct.src/M000031.html +18 -0
  131. data/doc/api/classes/Rumai/IXP/Struct.src/M000032.html +18 -0
  132. data/doc/api/classes/Rumai/IXP/Struct.src/M000033.html +18 -0
  133. data/doc/api/classes/Rumai/IXP/Struct.src/M000034.html +20 -0
  134. data/doc/api/classes/Rumai/IXP/Struct.src/M000035.html +57 -0
  135. data/doc/api/classes/Rumai/IXP/Struct/Field.html +304 -0
  136. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000036.html +21 -0
  137. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000037.html +21 -0
  138. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000038.html +20 -0
  139. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000039.html +29 -0
  140. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000040.html +18 -0
  141. data/doc/api/classes/Rumai/IXP/Struct/Field.src/M000041.html +18 -0
  142. data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.html +152 -0
  143. data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.src/M000043.html +24 -0
  144. data/doc/api/classes/Rumai/IXP/Struct/Field/CounteeField.src/M000044.html +25 -0
  145. data/doc/api/classes/Rumai/IXP/Struct/Field/CounterField.html +137 -0
  146. data/doc/api/classes/Rumai/IXP/Struct/Field/CounterField.src/M000042.html +18 -0
  147. data/doc/api/classes/Rumai/IXP/Tattach.html +120 -0
  148. data/doc/api/classes/Rumai/IXP/Tauth.html +119 -0
  149. data/doc/api/classes/Rumai/IXP/Tclunk.html +119 -0
  150. data/doc/api/classes/Rumai/IXP/Tcreate.html +120 -0
  151. data/doc/api/classes/Rumai/IXP/Terror.html +145 -0
  152. data/doc/api/classes/Rumai/IXP/Terror.src/M000075.html +18 -0
  153. data/doc/api/classes/Rumai/IXP/Tflush.html +119 -0
  154. data/doc/api/classes/Rumai/IXP/Topen.html +193 -0
  155. data/doc/api/classes/Rumai/IXP/Tread.html +119 -0
  156. data/doc/api/classes/Rumai/IXP/Tremove.html +119 -0
  157. data/doc/api/classes/Rumai/IXP/Tstat.html +119 -0
  158. data/doc/api/classes/Rumai/IXP/Tversion.html +137 -0
  159. data/doc/api/classes/Rumai/IXP/Twalk.html +120 -0
  160. data/doc/api/classes/Rumai/IXP/Twrite.html +120 -0
  161. data/doc/api/classes/Rumai/IXP/Twstat.html +119 -0
  162. data/doc/api/classes/Rumai/Node.html +461 -0
  163. data/doc/api/classes/Rumai/Node.src/M000097.html +18 -0
  164. data/doc/api/classes/Rumai/Node.src/M000098.html +18 -0
  165. data/doc/api/classes/Rumai/Node.src/M000099.html +22 -0
  166. data/doc/api/classes/Rumai/Node.src/M000100.html +18 -0
  167. data/doc/api/classes/Rumai/Node.src/M000101.html +18 -0
  168. data/doc/api/classes/Rumai/Node.src/M000102.html +18 -0
  169. data/doc/api/classes/Rumai/Node.src/M000103.html +18 -0
  170. data/doc/api/classes/Rumai/Node.src/M000104.html +22 -0
  171. data/doc/api/classes/Rumai/Node.src/M000105.html +18 -0
  172. data/doc/api/classes/Rumai/Node.src/M000106.html +18 -0
  173. data/doc/api/classes/Rumai/Node.src/M000107.html +18 -0
  174. data/doc/api/classes/Rumai/Node.src/M000108.html +18 -0
  175. data/doc/api/classes/Rumai/Node.src/M000109.html +18 -0
  176. data/doc/api/classes/Rumai/Node.src/M000110.html +18 -0
  177. data/doc/api/classes/Rumai/Node.src/M000111.html +18 -0
  178. data/doc/api/classes/Rumai/Node.src/M000112.html +20 -0
  179. data/doc/api/classes/Rumai/Node.src/M000113.html +27 -0
  180. data/doc/api/classes/Rumai/View.html +436 -0
  181. data/doc/api/classes/Rumai/View.src/M000131.html +18 -0
  182. data/doc/api/classes/Rumai/View.src/M000132.html +18 -0
  183. data/doc/api/classes/Rumai/View.src/M000133.html +18 -0
  184. data/doc/api/classes/Rumai/View.src/M000134.html +18 -0
  185. data/doc/api/classes/Rumai/View.src/M000135.html +18 -0
  186. data/doc/api/classes/Rumai/View.src/M000136.html +18 -0
  187. data/doc/api/classes/Rumai/View.src/M000137.html +18 -0
  188. data/doc/api/classes/Rumai/View.src/M000138.html +28 -0
  189. data/doc/api/classes/Rumai/View.src/M000139.html +18 -0
  190. data/doc/api/classes/Rumai/View.src/M000140.html +18 -0
  191. data/doc/api/classes/Rumai/View.src/M000141.html +18 -0
  192. data/doc/api/classes/Rumai/View.src/M000142.html +18 -0
  193. data/doc/api/classes/Rumai/View.src/M000143.html +29 -0
  194. data/doc/api/classes/Rumai/View.src/M000144.html +20 -0
  195. data/doc/api/classes/Rumai/View.src/M000145.html +33 -0
  196. data/doc/api/classes/Rumai/View.src/M000146.html +50 -0
  197. data/doc/api/classes/String.html +169 -0
  198. data/doc/api/classes/String.src/M000005.html +18 -0
  199. data/doc/api/classes/String.src/M000006.html +19 -0
  200. data/doc/api/classes/StringIO.html +120 -0
  201. data/doc/api/classes/Time.html +163 -0
  202. data/doc/api/classes/Time.src/M000003.html +18 -0
  203. data/doc/api/classes/Time.src/M000004.html +18 -0
  204. data/doc/api/created.rid +1 -0
  205. data/doc/api/files/lib/rumai/fs_rb.html +115 -0
  206. data/doc/api/files/lib/rumai/ixp/message_rb.html +120 -0
  207. data/doc/api/files/lib/rumai/ixp/message_spec_rb.html +175 -0
  208. data/doc/api/files/lib/rumai/ixp/message_spec_rb.src/M000001.html +37 -0
  209. data/doc/api/files/lib/rumai/ixp/transport_rb.html +115 -0
  210. data/doc/api/files/lib/rumai/ixp_rb.html +116 -0
  211. data/doc/api/files/lib/rumai/nfo_rb.html +107 -0
  212. data/doc/api/files/lib/rumai/wm_rb.html +115 -0
  213. data/doc/api/files/lib/rumai_rb.html +115 -0
  214. data/doc/api/fr_class_index.html +82 -0
  215. data/doc/api/fr_file_index.html +34 -0
  216. data/doc/api/fr_method_index.html +172 -0
  217. data/doc/api/index.html +24 -0
  218. data/doc/api/rdoc-style.css +208 -0
  219. data/doc/guide.erb +390 -0
  220. data/doc/guide.html +2035 -0
  221. data/doc/index.html +2035 -0
  222. data/lib/rumai.rb +9 -0
  223. data/lib/rumai/fs.rb +170 -0
  224. data/lib/rumai/ixp.rb +9 -0
  225. data/lib/rumai/ixp/message.rb +642 -0
  226. data/lib/rumai/ixp/message_spec.rb +247 -0
  227. data/lib/rumai/ixp/transport.rb +382 -0
  228. data/lib/rumai/nfo.rb +24 -0
  229. data/lib/rumai/wm.rb +780 -0
  230. metadata +320 -0
data/lib/rumai.rb ADDED
@@ -0,0 +1,9 @@
1
+ # Rumai: Ruby interface to wmii.
2
+ #--
3
+ # Copyright 2007 Suraj N. Kurapati
4
+ # See the file named LICENSE for details.
5
+
6
+ $:.unshift __FILE__.sub(/.rb$/, '')
7
+
8
+ require 'fs'
9
+ require 'wm'
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,9 @@
1
+ # An interface to wmii's IXP library for Rumai.
2
+ #--
3
+ # Copyright 2007 Suraj N. Kurapati
4
+ # See the file named LICENSE for details.
5
+
6
+ $:.unshift __FILE__.sub(/.rb$/, '')
7
+
8
+ require 'message'
9
+ require 'transport'
@@ -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