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