diff options
| author | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-07-01 03:39:56 +0000 |
|---|---|---|
| committer | Diego Nehab <diego@tecgraf.puc-rio.br> | 2004-07-01 03:39:56 +0000 |
| commit | 1812d6ce150d9661bac858b82f81e133409b8409 (patch) | |
| tree | edf72005b6b8c83af686dbf155c9bc333a53cdef | |
| parent | 7115c12fbc9aae1cd46fdf049697a27fb996181a (diff) | |
| download | luasocket-1812d6ce150d9661bac858b82f81e133409b8409.tar.gz luasocket-1812d6ce150d9661bac858b82f81e133409b8409.tar.bz2 luasocket-1812d6ce150d9661bac858b82f81e133409b8409.zip | |
Adjusted lp.lua.
| -rw-r--r-- | etc/lp.lua | 285 |
1 files changed, 96 insertions, 189 deletions
| @@ -1,17 +1,40 @@ | |||
| 1 | |||
| 2 | --[[ | ||
| 3 | if you have any questions RFC 1179 | ||
| 4 | ]] | ||
| 1 | -- make sure LuaSocket is loaded | 5 | -- make sure LuaSocket is loaded |
| 2 | local socket = require("socket") | 6 | local socket = require("socket") |
| 3 | local ltn12 = require("ltn12") | 7 | local ltn12 = require("ltn12") |
| 4 | local lp = {} | 8 | local test = socket.try |
| 5 | --socket.lp = lp | ||
| 6 | -- make all module globals fall into lp namespace | ||
| 7 | setmetatable(lp, { __index = _G }) | ||
| 8 | setfenv(1, lp) | ||
| 9 | 9 | ||
| 10 | -- default port | 10 | -- default port |
| 11 | PORT = 515 | 11 | PORT = 515 |
| 12 | SERVER = os.getenv("SERVER_NAME") or os.getenv("COMPUTERNAME") or "localhost" | 12 | SERVER = os.getenv("SERVER_NAME") or os.getenv("COMPUTERNAME") or "localhost" |
| 13 | PRINTER = os.getenv("PRINTER") or "printer" | 13 | PRINTER = os.getenv("PRINTER") or "printer" |
| 14 | 14 | ||
| 15 | local function connect(localhost, option) | ||
| 16 | local host = option.host or SERVER | ||
| 17 | local port = option.port or PORT | ||
| 18 | local skt | ||
| 19 | local try = socket.newtry(function() if skt then skt:close() end end) | ||
| 20 | if option.localbind then | ||
| 21 | -- bind to a local port (if we can) | ||
| 22 | local localport = 721 | ||
| 23 | repeat | ||
| 24 | skt = test(socket.tcp()) | ||
| 25 | try(skt:settimeout(30)) | ||
| 26 | local done, err = skt:bind(localhost, localport) | ||
| 27 | if not done then | ||
| 28 | localport = localport + 1 | ||
| 29 | skt:close() | ||
| 30 | skt = nil | ||
| 31 | else break end | ||
| 32 | until localport > 731 | ||
| 33 | test(skt, err) | ||
| 34 | else skt = test(socket.tcp()) end | ||
| 35 | try(skt:connect(host, port)) | ||
| 36 | return { skt = skt, try = try } | ||
| 37 | end | ||
| 15 | 38 | ||
| 16 | --[[ | 39 | --[[ |
| 17 | RFC 1179 | 40 | RFC 1179 |
| @@ -50,23 +73,16 @@ RFC 1179 | |||
| 50 | contain ASCII HT control characters. | 73 | contain ASCII HT control characters. |
| 51 | ]] | 74 | ]] |
| 52 | 75 | ||
| 53 | |||
| 54 | -- gets server acknowledement | 76 | -- gets server acknowledement |
| 55 | local function recv_ack(connection) | 77 | local function recv_ack(con) |
| 56 | local code, current, separator, _ | 78 | local ack = con.skt:receive(1) |
| 57 | local ack = socket.try(connection:receive(1)) | 79 | con.try(string.char(0) == ack, "failed to receive server acknowledement") |
| 58 | if string.char(0) ~= ack then | ||
| 59 | connection:close(); error"failed to receive server acknowledement" | ||
| 60 | end | ||
| 61 | end | 80 | end |
| 62 | 81 | ||
| 63 | -- sends client acknowledement | 82 | -- sends client acknowledement |
| 64 | local function send_ack(connection) | 83 | local function send_ack(con) |
| 65 | local sent = socket.try(connection:send(string.char(0))) | 84 | local sent = con.skt:send(string.char(0)) |
| 66 | if not sent or sent ~= 1 then | 85 | con.try(sent == 1, "failed to send acknowledgement") |
| 67 | connection:close(); | ||
| 68 | error"failed to send acknowledgement" | ||
| 69 | end | ||
| 70 | end | 86 | end |
| 71 | 87 | ||
| 72 | -- sends queue request | 88 | -- sends queue request |
| @@ -86,14 +102,12 @@ end | |||
| 86 | -- octet from the daemon. A positive acknowledgement is an octet of | 102 | -- octet from the daemon. A positive acknowledgement is an octet of |
| 87 | -- zero bits. A negative acknowledgement is an octet of any other | 103 | -- zero bits. A negative acknowledgement is an octet of any other |
| 88 | -- pattern. | 104 | -- pattern. |
| 89 | local function send_queue(connection,queue) | 105 | local function send_queue(con, queue) |
| 90 | if not queue then queue=PRINTER end | 106 | queue = queue or PRINTER |
| 91 | local str = string.format("\2%s\10",queue) | 107 | local str = string.format("\2%s\10", queue) |
| 92 | local sent = socket.try(connection:send(str)) | 108 | local sent = con.skt:send(str) |
| 93 | if not sent or sent ~= string.len(str) then | 109 | con.try(sent == string.len(str), "failed to send print request") |
| 94 | error "failed to send print request" | 110 | recv_ack(con) |
| 95 | end | ||
| 96 | recv_ack(connection) | ||
| 97 | end | 111 | end |
| 98 | 112 | ||
| 99 | -- sends control file | 113 | -- sends control file |
| @@ -145,48 +159,36 @@ end | |||
| 145 | -- processing must occur at this point. | 159 | -- processing must occur at this point. |
| 146 | 160 | ||
| 147 | 161 | ||
| 148 | local function send_hdr(connection,control) | 162 | local function send_hdr(con, control) |
| 149 | local sent = socket.try(connection:send(control)) | 163 | local sent = con.skt:send(control) |
| 150 | if not sent or sent < 1 then | 164 | con.try(sent and sent >= 1 , "failed to send header file") |
| 151 | error "failed to send file" | 165 | recv_ack(con) |
| 152 | end | ||
| 153 | recv_ack(connection) | ||
| 154 | end | 166 | end |
| 155 | 167 | ||
| 156 | 168 | local function send_control(con, control) | |
| 157 | local function send_control(connection,control) | 169 | local sent = con:send(control) |
| 158 | local sent = socket.try(connection:send(control)) | 170 | con.try(sent and sent >= 1, "failed to send control file") |
| 159 | if not sent or sent < 1 then | 171 | send_ack(con) |
| 160 | error "failed to send file" | ||
| 161 | end | ||
| 162 | send_ack(connection) | ||
| 163 | end | 172 | end |
| 164 | 173 | ||
| 165 | local function send_data(connection,fh,size) | 174 | local function send_data(con,fh,size) |
| 166 | -- local sink = socket.sink("keep-open", connection) | 175 | local buf |
| 167 | -- ltn12.pump.all(source, sink) | ||
| 168 | local buf, st, message | ||
| 169 | st = true | ||
| 170 | while size > 0 do | 176 | while size > 0 do |
| 171 | buf,message = fh:read(8192) | 177 | buf,message = fh:read(8192) |
| 172 | if buf then | 178 | if buf then |
| 173 | st = socket.try(connection:send(buf)) | 179 | st = con.try(con:send(buf)) |
| 174 | size = size - st | 180 | size = size - st |
| 175 | else | 181 | else |
| 176 | if size ~= 0 then | 182 | con.try(size == 0, "file size mismatch") |
| 177 | connection:close() | ||
| 178 | return nil, "file size mismatch" | ||
| 179 | end | ||
| 180 | end | 183 | end |
| 181 | end | 184 | end |
| 182 | send_ack(connection) | 185 | send_ack(con) |
| 183 | recv_ack(connection) | 186 | recv_ack(con) |
| 184 | return size,nil | 187 | return size |
| 185 | end | 188 | end |
| 186 | 189 | ||
| 187 | 190 | ||
| 188 | --[[ | 191 | --[[ |
| 189 | |||
| 190 | local control_dflt = { | 192 | local control_dflt = { |
| 191 | "H"..string.sub(socket.hostname,1,31).."\10", -- host | 193 | "H"..string.sub(socket.hostname,1,31).."\10", -- host |
| 192 | "C"..string.sub(socket.hostname,1,31).."\10", -- class | 194 | "C"..string.sub(socket.hostname,1,31).."\10", -- class |
| @@ -206,44 +208,15 @@ local control_dflt = { | |||
| 206 | "r"..file.."\10", -- fortran format | 208 | "r"..file.."\10", -- fortran format |
| 207 | "U"..file.."\10", -- Unlink (data file only) | 209 | "U"..file.."\10", -- Unlink (data file only) |
| 208 | } | 210 | } |
| 209 | |||
| 210 | ]] | 211 | ]] |
| 211 | 212 | ||
| 212 | -- generate a varying job number | 213 | -- generate a varying job number |
| 213 | local function getjobno(connection) | 214 | local seq = 0 |
| 214 | -- print(math.mod(socket.time() * 1000, port)) -- ok for windows | 215 | local function newjob(connection) |
| 215 | -- print(os.time() / port,math.random(0,999)) | 216 | seq = seq + 1 |
| 216 | return math.random(0,999) | 217 | return math.floor(socket.gettime() * 1000 + seq) |
| 217 | end | 218 | end |
| 218 | 219 | ||
| 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 | 220 | ||
| 248 | local format_codes = { | 221 | local format_codes = { |
| 249 | binary = 'l', | 222 | binary = 'l', |
| @@ -258,43 +231,33 @@ local format_codes = { | |||
| 258 | f = 'f' | 231 | f = 'f' |
| 259 | } | 232 | } |
| 260 | 233 | ||
| 261 | lp.send = socket.protect(function(file, option) | 234 | -- lp.send |
| 262 | if not file then error "invalid file name" end | 235 | |
| 263 | if not option or type(option) ~= "table" then error "invalid options" end | 236 | send = socket.protect(function(file, option) |
| 264 | local fh = socket.try(io.open(file,"rb")) | 237 | test(file, "invalid file name") |
| 265 | -- get total size | 238 | test(option and type(option) == "table", "invalid options") |
| 266 | local datafile_size = fh:seek("end") | 239 | local fh = test(io.open(file,"rb")) |
| 267 | -- go back to start of file | 240 | local datafile_size = fh:seek("end") -- get total size |
| 268 | fh:seek("set") | 241 | fh:seek("set") -- go back to start of file |
| 269 | math.randomseed(socket.time() * 1000) | 242 | local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") |
| 270 | local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") or "localhost" | 243 | or "localhost" |
| 271 | 244 | local con = connect(localhost, option) | |
| 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 | 245 | -- format the control file |
| 277 | local jobno = getjobno(connection) | 246 | local jobno = newjob() |
| 278 | local localip = socket.dns.toip(localhost) | 247 | local localip = socket.dns.toip(localhost) |
| 279 | localhost = string.sub(localhost,1,31) | 248 | localhost = string.sub(localhost,1,31) |
| 280 | 249 | local user = string.sub(option.user or os.getenv("LPRUSER") or | |
| 281 | local user = string.sub(option.user or os.getenv("LPRUSER") or os.getenv("USERNAME") | 250 | os.getenv("USERNAME") or os.getenv("USER") or "anonymous", 1,31) |
| 282 | or os.getenv("USER") or "anonymous",1,31) | ||
| 283 | |||
| 284 | local lpfile = string.format("dfA%3.3d%-s", jobno, localhost); | 251 | local lpfile = string.format("dfA%3.3d%-s", jobno, localhost); |
| 285 | |||
| 286 | local fmt = format_codes[option.format] or 'l' | 252 | local fmt = format_codes[option.format] or 'l' |
| 287 | |||
| 288 | local class = string.sub(option.class or localip or localhost,1,31) | 253 | local class = string.sub(option.class or localip or localhost,1,31) |
| 289 | |||
| 290 | local _,_,ctlfn = string.find(file,".*[%/%\\](.*)") | 254 | local _,_,ctlfn = string.find(file,".*[%/%\\](.*)") |
| 291 | ctlfn = string.sub(ctlfn or file,1,131) | 255 | ctlfn = string.sub(ctlfn or file,1,131) |
| 292 | |||
| 293 | local cfile = | 256 | local cfile = |
| 294 | string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n", | 257 | string.format("H%-s\nC%-s\nJ%-s\nP%-s\n%.1s%-s\nU%-s\nN%-s\n", |
| 295 | localhost, | 258 | localhost, |
| 296 | class, | 259 | class, |
| 297 | option.job or ctlfn, | 260 | option.job or "LuaSocket", |
| 298 | user, | 261 | user, |
| 299 | fmt, lpfile, | 262 | fmt, lpfile, |
| 300 | lpfile, | 263 | lpfile, |
| @@ -307,96 +270,40 @@ lp.send = socket.protect(function(file, option) | |||
| 307 | cfile = cfile .. 'W'..tonumber(option,width)..'\10' | 270 | cfile = cfile .. 'W'..tonumber(option,width)..'\10' |
| 308 | end | 271 | end |
| 309 | 272 | ||
| 310 | connection:settimeout(option.timeout or 65) | 273 | con.skt:settimeout(option.timeout or 65) |
| 311 | |||
| 312 | -- send the queue header | 274 | -- send the queue header |
| 313 | send_queue(connection,option.queue) | 275 | send_queue(con, option.queue) |
| 314 | |||
| 315 | -- send the control file header | 276 | -- send the control file header |
| 316 | local cfilecmd = string.format("\2%d cfA%3.3d%-s\n",string.len(cfile), jobno, localhost); | 277 | local cfilecmd = string.format("\2%d cfA%3.3d%-s\n",string.len(cfile), jobno, localhost); |
| 317 | send_hdr(connection,cfilecmd) | 278 | send_hdr(con,cfilecmd) |
| 318 | 279 | ||
| 319 | -- send the control file | 280 | -- send the control file |
| 320 | send_control(connection,cfile) | 281 | send_control(con,cfile) |
| 321 | 282 | ||
| 322 | -- send the data file header | 283 | -- send the data file header |
| 323 | local dfilecmd = string.format("\3%d dfA%3.3d%-s\n",datafile_size, jobno, localhost); | 284 | local dfilecmd = string.format("\3%d dfA%3.3d%-s\n",datafile_size, jobno, localhost); |
| 324 | send_hdr(connection,dfilecmd) | 285 | send_hdr(con,dfilecmd) |
| 325 | 286 | ||
| 326 | -- send the data file | 287 | -- send the data file |
| 327 | send_data(connection,fh,datafile_size) | 288 | send_data(con,fh,datafile_size) |
| 328 | fh:close() | 289 | fh:close() |
| 329 | connection:close(); | 290 | con.skt:close(); |
| 330 | return datafile_size | 291 | return jobno, datafile_size |
| 331 | end) | 292 | end) |
| 332 | 293 | ||
| 333 | 294 | -- | |
| 334 | --socket.lpq({host=,queue=printer|'*', format='l'|'s', list=}) | 295 | -- lp.query({host=,queue=printer|'*', format='l'|'s', list=}) |
| 335 | lp.query = socket.protect(function(p) | 296 | -- |
| 336 | if not p then p={} end | 297 | query = socket.protect(function(p) |
| 337 | local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") or "localhost" | 298 | p = p or {} |
| 338 | local connection = getcon(localhost,p) | 299 | local localhost = socket.dns.gethostname() or os.getenv("COMPUTERNAME") |
| 339 | local fmt,data | 300 | or "localhost" |
| 301 | local con = connect(localhost,p) | ||
| 302 | local fmt | ||
| 340 | if string.sub(p.format or 's',1,1) == 's' then fmt = 3 else fmt = 4 end | 303 | 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 ""))) | 304 | con.try(con.skt:send(string.format("%c%s %s\n", fmt, p.queue or "*", |
| 342 | local data = socket.try(connection:receive("*a")) | 305 | p.list or ""))) |
| 343 | io.write(data) | 306 | local data = ltry(connection:receive("*a")) |
| 344 | connection:close() | 307 | con.skt:close() |
| 345 | return tostring(string.len(data)) | 308 | return data |
| 346 | end) | 309 | 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 | ||
