Mercurial > luasocket
comparison etc/lp.lua @ 0:4b915342e2a8
LuaSocket 2.0.2 + CMake build description.
author | Eric Wing <ewing . public |-at-| gmail . com> |
---|---|
date | Tue, 26 Aug 2008 18:40:01 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4b915342e2a8 |
---|---|
1 ----------------------------------------------------------------------------- | |
2 -- LPD support for the Lua language | |
3 -- LuaSocket toolkit. | |
4 -- Author: David Burgess | |
5 -- Modified by Diego Nehab, but David is in charge | |
6 -- RCS ID: $Id: lp.lua,v 1.14 2005/11/21 07:04:44 diego Exp $ | |
7 ----------------------------------------------------------------------------- | |
8 --[[ | |
9 if you have any questions: RFC 1179 | |
10 ]] | |
11 -- make sure LuaSocket is loaded | |
12 local io = require("io") | |
13 local base = _G | |
14 local os = require("os") | |
15 local math = require("math") | |
16 local string = require("string") | |
17 local socket = require("socket") | |
18 local ltn12 = require("ltn12") | |
19 module("socket.lp") | |
20 | |
21 -- default port | |
22 PORT = 515 | |
23 SERVER = os.getenv("SERVER_NAME") or os.getenv("COMPUTERNAME") or "localhost" | |
24 PRINTER = os.getenv("PRINTER") or "printer" | |
25 | |
26 local function connect(localhost, option) | |
27 local host = option.host or SERVER | |
28 local port = option.port or PORT | |
29 local skt | |
30 local try = socket.newtry(function() if skt then skt:close() end end) | |
31 if option.localbind then | |
32 -- bind to a local port (if we can) | |
33 local localport = 721 | |
34 local done, err | |
35 repeat | |
36 skt = socket.try(socket.tcp()) | |
37 try(skt:settimeout(30)) | |
38 done, err = skt:bind(localhost, localport) | |
39 if not done then | |
40 localport = localport + 1 | |
41 skt:close() | |
42 skt = nil | |
43 else break end | |
44 until localport > 731 | |
45 socket.try(skt, err) | |
46 else skt = socket.try(socket.tcp()) end | |
47 try(skt:connect(host, port)) | |
48 return { skt = skt, try = try } | |
49 end | |
50 | |
51 --[[ | |
52 RFC 1179 | |
53 5.3 03 - Send queue state (short) | |
54 | |
55 +----+-------+----+------+----+ | |
56 | 03 | Queue | SP | List | LF | | |
57 +----+-------+----+------+----+ | |
58 Command code - 3 | |
59 Operand 1 - Printer queue name | |
60 Other operands - User names or job numbers | |
61 | |
62 If the user names or job numbers or both are supplied then only those | |
63 jobs for those users or with those numbers will be sent. | |
64 | |
65 The response is an ASCII stream which describes the printer queue. | |
66 The stream continues until the connection closes. Ends of lines are | |
67 indicated with ASCII LF control characters. The lines may also | |
68 contain ASCII HT control characters. | |
69 | |
70 5.4 04 - Send queue state (long) | |
71 | |
72 +----+-------+----+------+----+ | |
73 | 04 | Queue | SP | List | LF | | |
74 +----+-------+----+------+----+ | |
75 Command code - 4 | |
76 Operand 1 - Printer queue name | |
77 Other operands - User names or job numbers | |
78 | |
79 If the user names or job numbers or both are supplied then only those | |
80 jobs for those users or with those numbers will be sent. | |
81 | |
82 The response is an ASCII stream which describes the printer queue. | |
83 The stream continues until the connection closes. Ends of lines are | |
84 indicated with ASCII LF control characters. The lines may also | |
85 contain ASCII HT control characters. | |
86 ]] | |
87 | |
88 -- gets server acknowledement | |
89 local function recv_ack(con) | |
90 local ack = con.skt:receive(1) | |
91 con.try(string.char(0) == ack, "failed to receive server acknowledgement") | |
92 end | |
93 | |
94 -- sends client acknowledement | |
95 local function send_ack(con) | |
96 local sent = con.skt:send(string.char(0)) | |
97 con.try(sent == 1, "failed to send acknowledgement") | |
98 end | |
99 | |
100 -- sends queue request | |
101 -- 5.2 02 - Receive a printer job | |
102 -- | |
103 -- +----+-------+----+ | |
104 -- | 02 | Queue | LF | | |
105 -- +----+-------+----+ | |
106 -- Command code - 2 | |
107 -- Operand - Printer queue name | |
108 -- | |
109 -- Receiving a job is controlled by a second level of commands. The | |
110 -- daemon is given commands by sending them over the same connection. | |
111 -- The commands are described in the next section (6). | |
112 -- | |
113 -- After this command is sent, the client must read an acknowledgement | |
114 -- octet from the daemon. A positive acknowledgement is an octet of | |
115 -- zero bits. A negative acknowledgement is an octet of any other | |
116 -- pattern. | |
117 local function send_queue(con, queue) | |
118 queue = queue or PRINTER | |
119 local str = string.format("\2%s\10", queue) | |
120 local sent = con.skt:send(str) | |
121 con.try(sent == string.len(str), "failed to send print request") | |
122 recv_ack(con) | |
123 end | |
124 | |
125 -- sends control file | |
126 -- 6.2 02 - Receive control file | |
127 -- | |
128 -- +----+-------+----+------+----+ | |
129 -- | 02 | Count | SP | Name | LF | | |
130 -- +----+-------+----+------+----+ | |
131 -- Command code - 2 | |
132 -- Operand 1 - Number of bytes in control file | |
133 -- Operand 2 - Name of control file | |
134 -- | |
135 -- The control file must be an ASCII stream with the ends of lines | |
136 -- indicated by ASCII LF. The total number of bytes in the stream is | |
137 -- sent as the first operand. The name of the control file is sent as | |
138 -- the second. It should start with ASCII "cfA", followed by a three | |
139 -- digit job number, followed by the host name which has constructed the | |
140 -- control file. Acknowledgement processing must occur as usual after | |
141 -- the command is sent. | |
142 -- | |
143 -- The next "Operand 1" octets over the same TCP connection are the | |
144 -- intended contents of the control file. Once all of the contents have | |
145 -- been delivered, an octet of zero bits is sent as an indication that | |
146 -- the file being sent is complete. A second level of acknowledgement | |
147 -- processing must occur at this point. | |
148 | |
149 -- sends data file | |
150 -- 6.3 03 - Receive data file | |
151 -- | |
152 -- +----+-------+----+------+----+ | |
153 -- | 03 | Count | SP | Name | LF | | |
154 -- +----+-------+----+------+----+ | |
155 -- Command code - 3 | |
156 -- Operand 1 - Number of bytes in data file | |
157 -- Operand 2 - Name of data file | |
158 -- | |
159 -- The data file may contain any 8 bit values at all. The total number | |
160 -- of bytes in the stream may be sent as the first operand, otherwise | |
161 -- the field should be cleared to 0. The name of the data file should | |
162 -- start with ASCII "dfA". This should be followed by a three digit job | |
163 -- number. The job number should be followed by the host name which has | |
164 -- constructed the data file. Interpretation of the contents of the | |
165 -- data file is determined by the contents of the corresponding control | |
166 -- file. If a data file length has been specified, the next "Operand 1" | |
167 -- octets over the same TCP connection are the intended contents of the | |
168 -- data file. In this case, once all of the contents have been | |
169 -- delivered, an octet of zero bits is sent as an indication that the | |
170 -- file being sent is complete. A second level of acknowledgement | |
171 -- processing must occur at this point. | |
172 | |
173 | |
174 local function send_hdr(con, control) | |
175 local sent = con.skt:send(control) | |
176 con.try(sent and sent >= 1 , "failed to send header file") | |
177 recv_ack(con) | |
178 end | |
179 | |
180 local function send_control(con, control) | |
181 local sent = con.skt:send(control) | |
182 con.try(sent and sent >= 1, "failed to send control file") | |
183 send_ack(con) | |
184 end | |
185 | |
186 local function send_data(con,fh,size) | |
187 local buf | |
188 while size > 0 do | |
189 buf,message = fh:read(8192) | |
190 if buf then | |
191 st = con.try(con.skt:send(buf)) | |
192 size = size - st | |
193 else | |
194 con.try(size == 0, "file size mismatch") | |
195 end | |
196 end | |
197 recv_ack(con) -- note the double acknowledgement | |
198 send_ack(con) | |
199 recv_ack(con) | |
200 return size | |
201 end | |
202 | |
203 | |
204 --[[ | |
205 local control_dflt = { | |
206 "H"..string.sub(socket.hostname,1,31).."\10", -- host | |
207 "C"..string.sub(socket.hostname,1,31).."\10", -- class | |
208 "J"..string.sub(filename,1,99).."\10", -- jobname | |
209 "L"..string.sub(user,1,31).."\10", -- print banner page | |
210 "I"..tonumber(indent).."\10", -- indent column count ('f' only) | |
211 "M"..string.sub(mail,1,128).."\10", -- mail when printed user@host | |
212 "N"..string.sub(filename,1,131).."\10", -- name of source file | |
213 "P"..string.sub(user,1,31).."\10", -- user name | |
214 "T"..string.sub(title,1,79).."\10", -- title for banner ('p' only) | |
215 "W"..tonumber(width or 132).."\10", -- width of print f,l,p only | |
216 | |
217 "f"..file.."\10", -- formatted print (remove control chars) | |
218 "l"..file.."\10", -- print | |
219 "o"..file.."\10", -- postscript | |
220 "p"..file.."\10", -- pr format - requires T, L | |
221 "r"..file.."\10", -- fortran format | |
222 "U"..file.."\10", -- Unlink (data file only) | |
223 } | |
224 ]] | |
225 | |
226 -- generate a varying job number | |
227 local seq = 0 | |
228 local function newjob(connection) | |
229 seq = seq + 1 | |
230 return math.floor(socket.gettime() * 1000 + seq)%1000 | |
231 end | |
232 | |
233 | |
234 local format_codes = { | |
235 binary = 'l', | |
236 text = 'f', | |
237 ps = 'o', | |
238 pr = 'p', | |
239 fortran = 'r', | |
240 l = 'l', | |
241 r = 'r', | |
242 o = 'o', | |
243 p = 'p', | |
244 f = 'f' | |
245 } | |
246 | |
247 -- lp.send{option} | |
248 -- requires option.file | |
249 | |
250 send = socket.protect(function(option) | |
251 socket.try(option and base.type(option) == "table", "invalid options") | |
252 local file = option.file | |
253 socket.try(file, "invalid file name") | |
254 local fh = socket.try(io.open(file,"rb")) | |
255 local datafile_size = fh:seek("end") -- get total size | |
256 fh:seek("set") -- go back to start of file | |
257 local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") | |
258 or "localhost" | |
259 local con = connect(localhost, option) | |
260 -- format the control file | |
261 local jobno = newjob() | |
262 local localip = socket.dns.toip(localhost) | |
263 localhost = string.sub(localhost,1,31) | |
264 local user = string.sub(option.user or os.getenv("LPRUSER") or | |
265 os.getenv("USERNAME") or os.getenv("USER") or "anonymous", 1,31) | |
266 local lpfile = string.format("dfA%3.3d%-s", jobno, localhost); | |
267 local fmt = format_codes[option.format] or 'l' | |
268 local class = string.sub(option.class or localip or localhost,1,31) | |
269 local _,_,ctlfn = string.find(file,".*[%/%\\](.*)") | |
270 ctlfn = string.sub(ctlfn or file,1,131) | |
271 local cfile = | |
272 string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n", | |
273 localhost, | |
274 class, | |
275 option.job or "LuaSocket", | |
276 user, | |
277 fmt, lpfile, | |
278 lpfile, | |
279 ctlfn); -- mandatory part of ctl file | |
280 if (option.banner) then cfile = cfile .. 'L'..user..'\10' end | |
281 if (option.indent) then cfile = cfile .. 'I'..base.tonumber(option.indent)..'\10' end | |
282 if (option.mail) then cfile = cfile .. 'M'..string.sub((option.mail),1,128)..'\10' end | |
283 if (fmt == 'p' and option.title) then cfile = cfile .. 'T'..string.sub((option.title),1,79)..'\10' end | |
284 if ((fmt == 'p' or fmt == 'l' or fmt == 'f') and option.width) then | |
285 cfile = cfile .. 'W'..base.tonumber(option,width)..'\10' | |
286 end | |
287 | |
288 con.skt:settimeout(option.timeout or 65) | |
289 -- send the queue header | |
290 send_queue(con, option.queue) | |
291 -- send the control file header | |
292 local cfilecmd = string.format("\2%d cfA%3.3d%-s\n",string.len(cfile), jobno, localhost); | |
293 send_hdr(con,cfilecmd) | |
294 | |
295 -- send the control file | |
296 send_control(con,cfile) | |
297 | |
298 -- send the data file header | |
299 local dfilecmd = string.format("\3%d dfA%3.3d%-s\n",datafile_size, jobno, localhost); | |
300 send_hdr(con,dfilecmd) | |
301 | |
302 -- send the data file | |
303 send_data(con,fh,datafile_size) | |
304 fh:close() | |
305 con.skt:close(); | |
306 return jobno, datafile_size | |
307 end) | |
308 | |
309 -- | |
310 -- lp.query({host=,queue=printer|'*', format='l'|'s', list=}) | |
311 -- | |
312 query = socket.protect(function(p) | |
313 p = p or {} | |
314 local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") | |
315 or "localhost" | |
316 local con = connect(localhost,p) | |
317 local fmt | |
318 if string.sub(p.format or 's',1,1) == 's' then fmt = 3 else fmt = 4 end | |
319 con.try(con.skt:send(string.format("%c%s %s\n", fmt, p.queue or "*", | |
320 p.list or ""))) | |
321 local data = con.try(con.skt:receive("*a")) | |
322 con.skt:close() | |
323 return data | |
324 end) |