diff options
author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-06-18 21:41:44 +0000 |
---|---|---|
committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-06-18 21:41:44 +0000 |
commit | 7ed89c97f760600df238f9853ee453570935870f (patch) | |
tree | a17573a43815071e35f85557519fefca739ef7ef /etc | |
parent | ac4aac0909da26befaaeb6b415f66cf35b6980e0 (diff) | |
download | luasocket-7ed89c97f760600df238f9853ee453570935870f.tar.gz luasocket-7ed89c97f760600df238f9853ee453570935870f.tar.bz2 luasocket-7ed89c97f760600df238f9853ee453570935870f.zip |
2.0 alpha RELEASED!
Diffstat (limited to 'etc')
-rw-r--r-- | etc/README | 20 | ||||
-rw-r--r-- | etc/lp.lua | 402 |
2 files changed, 405 insertions, 17 deletions
@@ -1,24 +1,10 @@ | |||
1 | This directory contains code that is more useful than the examples. This code | 1 | This directory contains code that is more useful than the examples. This code |
2 | *is* supported. | 2 | *is* supported. |
3 | 3 | ||
4 | lua.lua | 4 | lua.lua -- new require and requirelib implementations |
5 | 5 | ||
6 | These are modules to suport dynamic loading of LuaSocket by the stand alone | 6 | This is to support dynamic loading of LuaSocket. Check the INSTALL |
7 | Lua Interpreter with the use of new "require" and "requirelib" functions. | 7 | file for more information. |
8 | For my Mac OS X box, for instance, I place all files in | ||
9 | /Users/diego/tec/luasocket and set the following environment variables: | ||
10 | |||
11 | LUA_INIT=@/Users/diego/tec/luasocket/lua.lua | ||
12 | LUA_PATH=/Users/diego/tec/luasocket/?.lua;?.lua | ||
13 | LUA_PATHLIB=/Users/diego/tec/luasocket/?.dylib;?.dylib | ||
14 | |||
15 | With that, I can run any luasocket application with the command line: | ||
16 | |||
17 | lua <script> | ||
18 | |||
19 | as long as the script uses "require" to load the needed namespaces. | ||
20 | Much nicer than having to build a new executable just to initialize | ||
21 | LuaSocket! | ||
22 | 8 | ||
23 | tftp.lua -- Trivial FTP client | 9 | tftp.lua -- Trivial FTP client |
24 | 10 | ||
diff --git a/etc/lp.lua b/etc/lp.lua new file mode 100644 index 0000000..80acf86 --- /dev/null +++ b/etc/lp.lua | |||
@@ -0,0 +1,402 @@ | |||
1 | -- make sure LuaSocket is loaded | ||
2 | local socket = require("socket") | ||
3 | local ltn12 = require("ltn12") | ||
4 | local lp = {} | ||
5 | --socket.lp = lp | ||
6 | -- make all module globals fall into lp namespace | ||
7 | setmetatable(lp, { __index = _G }) | ||
8 | setfenv(1, lp) | ||
9 | |||
10 | -- default port | ||
11 | PORT = 515 | ||
12 | SERVER = os.getenv("SERVER_NAME") or os.getenv("COMPUTERNAME") or "localhost" | ||
13 | PRINTER = os.getenv("PRINTER") or "printer" | ||
14 | |||
15 | |||
16 | --[[ | ||
17 | RFC 1179 | ||
18 | 5.3 03 - Send queue state (short) | ||
19 | |||
20 | +----+-------+----+------+----+ | ||
21 | | 03 | Queue | SP | List | LF | | ||
22 | +----+-------+----+------+----+ | ||
23 | Command code - 3 | ||
24 | Operand 1 - Printer queue name | ||
25 | Other operands - User names or job numbers | ||
26 | |||
27 | If the user names or job numbers or both are supplied then only those | ||
28 | jobs for those users or with those numbers will be sent. | ||
29 | |||
30 | The response is an ASCII stream which describes the printer queue. | ||
31 | The stream continues until the connection closes. Ends of lines are | ||
32 | indicated with ASCII LF control characters. The lines may also | ||
33 | contain ASCII HT control characters. | ||
34 | |||
35 | 5.4 04 - Send queue state (long) | ||
36 | |||
37 | +----+-------+----+------+----+ | ||
38 | | 04 | Queue | SP | List | LF | | ||
39 | +----+-------+----+------+----+ | ||
40 | Command code - 4 | ||
41 | Operand 1 - Printer queue name | ||
42 | Other operands - User names or job numbers | ||
43 | |||
44 | If the user names or job numbers or both are supplied then only those | ||
45 | jobs for those users or with those numbers will be sent. | ||
46 | |||
47 | The response is an ASCII stream which describes the printer queue. | ||
48 | The stream continues until the connection closes. Ends of lines are | ||
49 | indicated with ASCII LF control characters. The lines may also | ||
50 | contain ASCII HT control characters. | ||
51 | ]] | ||
52 | |||
53 | |||
54 | -- gets server acknowledement | ||
55 | local function recv_ack(connection) | ||
56 | local code, current, separator, _ | ||
57 | local ack = socket.try(connection:receive(1)) | ||
58 | if string.char(0) ~= ack then | ||
59 | connection:close(); error"failed to receive server acknowledement" | ||
60 | end | ||
61 | end | ||
62 | |||
63 | -- sends client acknowledement | ||
64 | local function send_ack(connection) | ||
65 | local sent = socket.try(connection:send(string.char(0))) | ||
66 | if not sent or sent ~= 1 then | ||
67 | connection:close(); | ||
68 | error"failed to send acknowledgement" | ||
69 | end | ||
70 | end | ||
71 | |||
72 | -- sends queue request | ||
73 | -- 5.2 02 - Receive a printer job | ||
74 | -- | ||
75 | -- +----+-------+----+ | ||
76 | -- | 02 | Queue | LF | | ||
77 | -- +----+-------+----+ | ||
78 | -- Command code - 2 | ||
79 | -- Operand - Printer queue name | ||
80 | -- | ||
81 | -- Receiving a job is controlled by a second level of commands. The | ||
82 | -- daemon is given commands by sending them over the same connection. | ||
83 | -- The commands are described in the next section (6). | ||
84 | -- | ||
85 | -- After this command is sent, the client must read an acknowledgement | ||
86 | -- octet from the daemon. A positive acknowledgement is an octet of | ||
87 | -- zero bits. A negative acknowledgement is an octet of any other | ||
88 | -- pattern. | ||
89 | local function send_queue(connection,queue) | ||
90 | if not queue then queue=PRINTER end | ||
91 | local str = string.format("\2%s\10",queue) | ||
92 | local sent = socket.try(connection:send(str)) | ||
93 | if not sent or sent ~= string.len(str) then | ||
94 | error "failed to send print request" | ||
95 | end | ||
96 | recv_ack(connection) | ||
97 | end | ||
98 | |||
99 | -- sends control file | ||
100 | -- 6.2 02 - Receive control file | ||
101 | -- | ||
102 | -- +----+-------+----+------+----+ | ||
103 | -- | 02 | Count | SP | Name | LF | | ||
104 | -- +----+-------+----+------+----+ | ||
105 | -- Command code - 2 | ||
106 | -- Operand 1 - Number of bytes in control file | ||
107 | -- Operand 2 - Name of control file | ||
108 | -- | ||
109 | -- The control file must be an ASCII stream with the ends of lines | ||
110 | -- indicated by ASCII LF. The total number of bytes in the stream is | ||
111 | -- sent as the first operand. The name of the control file is sent as | ||
112 | -- the second. It should start with ASCII "cfA", followed by a three | ||
113 | -- digit job number, followed by the host name which has constructed the | ||
114 | -- control file. Acknowledgement processing must occur as usual after | ||
115 | -- the command is sent. | ||
116 | -- | ||
117 | -- The next "Operand 1" octets over the same TCP connection are the | ||
118 | -- intended contents of the control file. Once all of the contents have | ||
119 | -- been delivered, an octet of zero bits is sent as an indication that | ||
120 | -- the file being sent is complete. A second level of acknowledgement | ||
121 | -- processing must occur at this point. | ||
122 | |||
123 | -- sends data file | ||
124 | -- 6.3 03 - Receive data file | ||
125 | -- | ||
126 | -- +----+-------+----+------+----+ | ||
127 | -- | 03 | Count | SP | Name | LF | | ||
128 | -- +----+-------+----+------+----+ | ||
129 | -- Command code - 3 | ||
130 | -- Operand 1 - Number of bytes in data file | ||
131 | -- Operand 2 - Name of data file | ||
132 | -- | ||
133 | -- The data file may contain any 8 bit values at all. The total number | ||
134 | -- of bytes in the stream may be sent as the first operand, otherwise | ||
135 | -- the field should be cleared to 0. The name of the data file should | ||
136 | -- start with ASCII "dfA". This should be followed by a three digit job | ||
137 | -- number. The job number should be followed by the host name which has | ||
138 | -- constructed the data file. Interpretation of the contents of the | ||
139 | -- data file is determined by the contents of the corresponding control | ||
140 | -- file. If a data file length has been specified, the next "Operand 1" | ||
141 | -- octets over the same TCP connection are the intended contents of the | ||
142 | -- data file. In this case, once all of the contents have been | ||
143 | -- delivered, an octet of zero bits is sent as an indication that the | ||
144 | -- file being sent is complete. A second level of acknowledgement | ||
145 | -- processing must occur at this point. | ||
146 | |||
147 | |||
148 | local function send_hdr(connection,control) | ||
149 | local sent = socket.try(connection:send(control)) | ||
150 | if not sent or sent < 1 then | ||
151 | error "failed to send file" | ||
152 | end | ||
153 | recv_ack(connection) | ||
154 | end | ||
155 | |||
156 | |||
157 | local function send_control(connection,control) | ||
158 | local sent = socket.try(connection:send(control)) | ||
159 | if not sent or sent < 1 then | ||
160 | error "failed to send file" | ||
161 | end | ||
162 | send_ack(connection) | ||
163 | end | ||
164 | |||
165 | local function send_data(connection,fh,size) | ||
166 | -- local sink = socket.sink("keep-open", connection) | ||
167 | -- ltn12.pump.all(source, sink) | ||
168 | local buf, st, message | ||
169 | st = true | ||
170 | while size > 0 do | ||
171 | buf,message = fh:read(8192) | ||
172 | if buf then | ||
173 | st = socket.try(connection:send(buf)) | ||
174 | size = size - st | ||
175 | else | ||
176 | if size ~= 0 then | ||
177 | connection:close() | ||
178 | return nil, "file size mismatch" | ||
179 | end | ||
180 | end | ||
181 | end | ||
182 | send_ack(connection) | ||
183 | recv_ack(connection) | ||
184 | return size,nil | ||
185 | end | ||
186 | |||
187 | |||
188 | --[[ | ||
189 | |||
190 | local control_dflt = { | ||
191 | "H"..string.sub(socket.hostname,1,31).."\10", -- host | ||
192 | "C"..string.sub(socket.hostname,1,31).."\10", -- class | ||
193 | "J"..string.sub(filename,1,99).."\10", -- jobname | ||
194 | "L"..string.sub(user,1,31).."\10", -- print banner page | ||
195 | "I"..tonumber(indent).."\10", -- indent column count ('f' only) | ||
196 | "M"..string.sub(mail,1,128).."\10", -- mail when printed user@host | ||
197 | "N"..string.sub(filename,1,131).."\10", -- name of source file | ||
198 | "P"..string.sub(user,1,31).."\10", -- user name | ||
199 | "T"..string.sub(title,1,79).."\10", -- title for banner ('p' only) | ||
200 | "W"..tonumber(width or 132).."\10", -- width of print f,l,p only | ||
201 | |||
202 | "f"..file.."\10", -- formatted print (remove control chars) | ||
203 | "l"..file.."\10", -- print | ||
204 | "o"..file.."\10", -- postscript | ||
205 | "p"..file.."\10", -- pr format - requires T, L | ||
206 | "r"..file.."\10", -- fortran format | ||
207 | "U"..file.."\10", -- Unlink (data file only) | ||
208 | } | ||
209 | |||
210 | ]] | ||
211 | |||
212 | -- generate a varying job number | ||
213 | local function getjobno(connection) | ||
214 | -- print(math.mod(socket.time() * 1000, port)) -- ok for windows | ||
215 | -- print(os.time() / port,math.random(0,999)) | ||
216 | return math.random(0,999) | ||
217 | end | ||
218 | |||
219 | local function getcon(localhost,option) | ||
220 | local skt, st, message | ||
221 | local localport = 721 | ||
222 | if not option then | ||
223 | error('no options',0) | ||
224 | end | ||
225 | if option.localbind then | ||
226 | repeat | ||
227 | -- bind to a local port (if we can) | ||
228 | skt = socket.try(socket.tcp()) | ||
229 | skt:settimeout(30) | ||
230 | |||
231 | st, message = skt:bind(localhost,localport,-1); | ||
232 | -- print("bind",st,message) | ||
233 | if st then | ||
234 | st,message = skt:connect(option.host or SERVER, option.port or PORT) | ||
235 | -- print("connect",st,message) | ||
236 | end | ||
237 | -- print(st,localport,message) | ||
238 | if not st then | ||
239 | localport = localport + 1 | ||
240 | skt:close() | ||
241 | end | ||
242 | until st or localport > 731 or (not st and message ~= "local address already in use") | ||
243 | if st then return skt end | ||
244 | end | ||
245 | return socket.try(socket.connect(option.host or SERVER, option.port or PORT)) | ||
246 | end | ||
247 | |||
248 | local format_codes = { | ||
249 | binary = 'l', | ||
250 | text = 'f', | ||
251 | ps = 'o', | ||
252 | pr = 'p', | ||
253 | fortran = 'r', | ||
254 | l = 'l', | ||
255 | r = 'r', | ||
256 | o = 'o', | ||
257 | p = 'p', | ||
258 | f = 'f' | ||
259 | } | ||
260 | |||
261 | lp.send = socket.protect(function(file, option) | ||
262 | if not file then error "invalid file name" end | ||
263 | if not option or type(option) ~= "table" then error "invalid options" end | ||
264 | local fh = socket.try(io.open(file,"rb")) | ||
265 | -- get total size | ||
266 | local datafile_size = fh:seek("end") | ||
267 | -- go back to start of file | ||
268 | fh:seek("set") | ||
269 | math.randomseed(socket.time() * 1000) | ||
270 | local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") or "localhost" | ||
271 | |||
272 | -- local connection, message = skt:connect(option.host or SERVER, option.port or PORT) | ||
273 | |||
274 | local connection = getcon(localhost,option) | ||
275 | |||
276 | -- format the control file | ||
277 | local jobno = getjobno(connection) | ||
278 | local localip = socket.dns.toip(localhost) | ||
279 | localhost = string.sub(localhost,1,31) | ||
280 | |||
281 | local user = string.sub(option.user or os.getenv("LPRUSER") or os.getenv("USERNAME") | ||
282 | or os.getenv("USER") or "anonymous",1,31) | ||
283 | |||
284 | local lpfile = string.format("dfA%3.3d%-s", jobno, localhost); | ||
285 | |||
286 | local fmt = format_codes[option.format] or 'l' | ||
287 | |||
288 | local class = string.sub(option.class or localip or localhost,1,31) | ||
289 | |||
290 | local _,_,ctlfn = string.find(file,".*[%/%\\](.*)") | ||
291 | ctlfn = string.sub(ctlfn or file,1,131) | ||
292 | |||
293 | local cfile = | ||
294 | string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n", | ||
295 | localhost, | ||
296 | class, | ||
297 | option.job or ctlfn, | ||
298 | user, | ||
299 | fmt, lpfile, | ||
300 | lpfile, | ||
301 | ctlfn); -- mandatory part of ctl file | ||
302 | if (option.banner) then cfile = cfile .. 'L'..user..'\10' end | ||
303 | if (option.indent) then cfile = cfile .. 'I'..tonumber(option.indent)..'\10' end | ||
304 | if (option.mail) then cfile = cfile .. 'M'..string.sub((option.mail),1,128)..'\10' end | ||
305 | if (fmt == 'p' and option.title) then cfile = cfile .. 'T'..string.sub((option.title),1,79)..'\10' end | ||
306 | if ((fmt == 'p' or fmt == 'l' or fmt == 'f') and option.width) then | ||
307 | cfile = cfile .. 'W'..tonumber(option,width)..'\10' | ||
308 | end | ||
309 | |||
310 | connection:settimeout(option.timeout or 65) | ||
311 | |||
312 | -- send the queue header | ||
313 | send_queue(connection,option.queue) | ||
314 | |||
315 | -- send the control file header | ||
316 | local cfilecmd = string.format("\2%d cfA%3.3d%-s\n",string.len(cfile), jobno, localhost); | ||
317 | send_hdr(connection,cfilecmd) | ||
318 | |||
319 | -- send the control file | ||
320 | send_control(connection,cfile) | ||
321 | |||
322 | -- send the data file header | ||
323 | local dfilecmd = string.format("\3%d dfA%3.3d%-s\n",datafile_size, jobno, localhost); | ||
324 | send_hdr(connection,dfilecmd) | ||
325 | |||
326 | -- send the data file | ||
327 | send_data(connection,fh,datafile_size) | ||
328 | fh:close() | ||
329 | connection:close(); | ||
330 | return datafile_size | ||
331 | end) | ||
332 | |||
333 | |||
334 | --socket.lpq({host=,queue=printer|'*', format='l'|'s', list=}) | ||
335 | lp.query = socket.protect(function(p) | ||
336 | if not p then p={} end | ||
337 | local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") or "localhost" | ||
338 | local connection = getcon(localhost,p) | ||
339 | local fmt,data | ||
340 | if string.sub(p.format or 's',1,1) == 's' then fmt = 3 else fmt = 4 end | ||
341 | local sent = socket.try(connection:send(string.format("%c%s %s\n", fmt, p.queue or "*", p.list or ""))) | ||
342 | local data = socket.try(connection:receive("*a")) | ||
343 | io.write(data) | ||
344 | connection:close() | ||
345 | return tostring(string.len(data)) | ||
346 | end) | ||
347 | |||
348 | --for k,v in arg do print(k,v) end | ||
349 | local function usage() | ||
350 | print('\nUsage: lp filename [keyword=val...]\n') | ||
351 | print('Valid keywords are :') | ||
352 | print( | ||
353 | ' host=remote host or IP address (default "localhost")\n' .. | ||
354 | ' queue=remote queue or printer name (default "printer")\n' .. | ||
355 | ' port=remote port number (default 515)\n' .. | ||
356 | ' user=sending user name\n' .. | ||
357 | ' format=["binary" | "text" | "ps" | "pr" | "fortran"] (default "binary")\n' .. | ||
358 | ' banner=true|false\n' .. | ||
359 | ' indent=number of columns to indent\n' .. | ||
360 | ' mail=email of address to notify when print is complete\n' .. | ||
361 | ' title=title to use for "pr" format\n' .. | ||
362 | ' width=width for "text" or "pr" formats\n' .. | ||
363 | ' class=\n' .. | ||
364 | ' job=\n' .. | ||
365 | ' name=\n' .. | ||
366 | ' localbind=true|false\n' | ||
367 | ) | ||
368 | return nil | ||
369 | end | ||
370 | |||
371 | if not arg or not arg[1] then | ||
372 | return usage() | ||
373 | end | ||
374 | |||
375 | do | ||
376 | local s="opt = {" | ||
377 | for i = 2 , table.getn(arg), 1 do | ||
378 | s = s .. string.gsub(arg[i],"[%s%c%p]*([%w]*)=([\"]?[%w%s_!@#$%%^&*()<>:;]+[\"]\?\.?)","%1%=\"%2\",\n") | ||
379 | end | ||
380 | s = s .. "};\n" | ||
381 | assert(loadstring(s))(); | ||
382 | if not arg[2] then | ||
383 | return usage() | ||
384 | end | ||
385 | if arg[1] ~= "query" then | ||
386 | r,e=lp.send(arg[1],opt) | ||
387 | io.stderr:write(tostring(r or e),'\n') | ||
388 | else | ||
389 | r,e=lp.query(opt) | ||
390 | io.stderr:write(tostring(r or e),'\n') | ||
391 | end | ||
392 | end | ||
393 | |||
394 | -- trivial tests | ||
395 | --lua lp.lua lp.lua queue=default host=localhost | ||
396 | --lua lp.lua lp.lua queue=default host=localhost format=binary localbind=1 | ||
397 | --lua lp.lua query queue=default host=localhost | ||
398 | collectgarbage() | ||
399 | collectgarbage() | ||
400 | --print(socket.lp.query{host='localhost', queue="default"}) | ||
401 | |||
402 | return nil | ||