aboutsummaryrefslogtreecommitdiff
path: root/etc
diff options
context:
space:
mode:
authorDiego Nehab <diego@tecgraf.puc-rio.br>2004-06-18 21:41:44 +0000
committerDiego Nehab <diego@tecgraf.puc-rio.br>2004-06-18 21:41:44 +0000
commit7ed89c97f760600df238f9853ee453570935870f (patch)
treea17573a43815071e35f85557519fefca739ef7ef /etc
parentac4aac0909da26befaaeb6b415f66cf35b6980e0 (diff)
downloadluasocket-7ed89c97f760600df238f9853ee453570935870f.tar.gz
luasocket-7ed89c97f760600df238f9853ee453570935870f.tar.bz2
luasocket-7ed89c97f760600df238f9853ee453570935870f.zip
2.0 alpha RELEASED!
Diffstat (limited to 'etc')
-rw-r--r--etc/README20
-rw-r--r--etc/lp.lua402
2 files changed, 405 insertions, 17 deletions
diff --git a/etc/README b/etc/README
index d650843..c459a46 100644
--- a/etc/README
+++ b/etc/README
@@ -1,24 +1,10 @@
1This directory contains code that is more useful than the examples. This code 1This 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
6These are modules to suport dynamic loading of LuaSocket by the stand alone 6This is to support dynamic loading of LuaSocket. Check the INSTALL
7Lua Interpreter with the use of new "require" and "requirelib" functions. 7file for more information.
8For 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
15With that, I can run any luasocket application with the command line:
16
17 lua <script>
18
19as long as the script uses "require" to load the needed namespaces.
20Much nicer than having to build a new executable just to initialize
21LuaSocket!
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
2local socket = require("socket")
3local ltn12 = require("ltn12")
4local lp = {}
5--socket.lp = lp
6-- make all module globals fall into lp namespace
7setmetatable(lp, { __index = _G })
8setfenv(1, lp)
9
10-- default port
11PORT = 515
12SERVER = os.getenv("SERVER_NAME") or os.getenv("COMPUTERNAME") or "localhost"
13PRINTER = os.getenv("PRINTER") or "printer"
14
15
16--[[
17RFC 1179
185.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
355.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
55local 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
61end
62
63-- sends client acknowledement
64local 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
70end
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.
89local 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)
97end
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
148local 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)
154end
155
156
157local 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)
163end
164
165local 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
185end
186
187
188--[[
189
190local 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
213local 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)
217end
218
219local 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))
246end
247
248local 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
261lp.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
331end)
332
333
334--socket.lpq({host=,queue=printer|'*', format='l'|'s', list=})
335lp.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))
346end)
347
348--for k,v in arg do print(k,v) end
349local 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
369end
370
371if not arg or not arg[1] then
372 return usage()
373end
374
375do
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
392end
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
398collectgarbage()
399collectgarbage()
400--print(socket.lp.query{host='localhost', queue="default"})
401
402return nil