lua_rc: fix pause (use the right function as it exist now).
[vlc/solaris.git] / share / lua / intf / rc.lua
blob0eac503f1955ac13e898ee65a3e8a362ed14dd28
1 --[==========================================================================[
2 rc.lua: remote control module for VLC
3 --[==========================================================================[
4 Copyright (C) 2007-2009 the VideoLAN team
5 $Id$
7 Authors: Antoine Cellerier <dionoea at videolan dot org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 --]==========================================================================]
24 description=
25 [============================================================================[
26 Remote control interface for VLC
28 This is a modules/control/rc.c look alike (with a bunch of new features)
30 Use on local term:
31 vlc -I rc
32 Use on tcp connection:
33 vlc -I rc --lua-config "rc={host='localhost:4212'}"
34 Use on multiple hosts (term + 2 tcp ports):
35 vlc -I rc --lua-config "rc={hosts={'*console','localhost:4212','localhost:5678'}}"
37 Note:
38 -I rc and -I luarc are aliases for -I lua --lua-intf rc
40 Configuration options setable throught the --lua-config option are:
41 * hosts: A list of hosts to listen on.
42 * host: A host to listen on. (won't be used if `hosts' is set)
43 * eval: Add eval command to evaluate lua expressions. Set to any value to
44 enable.
45 The following can be set using the --lua-config option or in the interface
46 itself using the `set' command:
47 * prompt: The prompt.
48 * welcome: The welcome message.
49 * width: The default terminal width (used to format text).
50 * autocompletion: When issuing an unknown command, print a list of
51 possible commands to autocomplete with. (0 to disable,
52 1 to enable).
53 * autoalias: If autocompletion returns only one possibility, use it
54 (0 to disable, 1 to enable).
55 * flatplaylist: 0 to disable, 1 to enable.
56 ]============================================================================]
58 require("common")
59 skip = common.skip
60 skip2 = function(foo) return skip(skip(foo)) end
61 setarg = common.setarg
62 strip = common.strip
64 _ = vlc.gettext._
65 N_ = vlc.gettext.N_
67 --[[ Setup default environement ]]
68 env = { prompt = "> ";
69 width = 70;
70 autocompletion = 1;
71 autoalias = 1;
72 welcome = _("Remote control interface initialized. Type `help' for help.");
73 flatplaylist = 0;
76 --[[ Import custom environement variables from the command line config (if possible) ]]
77 for k,v in pairs(env) do
78 if config[k] then
79 if type(env[k]) == type(config[k]) then
80 env[k] = config[k]
81 vlc.msg.dbg("set environement variable `"..k.."' to "..tostring(env[k]))
82 else
83 vlc.msg.err("environement variable `"..k.."' should be of type "..type(env[k])..". config value will be discarded.")
84 end
85 end
86 end
88 --[[ Command functions ]]
89 function set_env(name,client,value)
90 if value then
91 local var,val = split_input(value)
92 if val then
93 local s = string.gsub(val,"\"(.*)\"","%1")
94 if type(client.env[var])==type(1) then
95 client.env[var] = tonumber(s)
96 else
97 client.env[var] = s
98 end
99 else
100 client:append( tostring(client.env[var]) )
102 else
103 for e,v in common.pairs_sorted(client.env) do
104 client:append(e.."="..v)
109 function save_env(name,client,value)
110 env = common.table_copy(client.env)
113 function alias(client,value)
114 if value then
115 local var,val = split_input(value)
116 if commands[var] and type(commands[var]) ~= type("") then
117 client:append("Error: cannot use a primary command as an alias name")
118 else
119 if commands[val] then
120 commands[var]=val
121 else
122 client:append("Error: unknown primary command `"..val.."'.")
125 else
126 for c,v in common.pairs_sorted(commands) do
127 if type(v)==type("") then
128 client:append(c.."="..v)
134 function fixme(name,client)
135 client:append( "FIXME: unimplemented command `"..name.."'." )
138 function logout(name,client)
139 if client.type == host.client_type.net then
140 client:send("Bye-bye!")
141 client:del()
142 else
143 client:append("Error: Can't logout of stdin/stdout. Use quit or shutdown to close VLC.")
147 function shutdown(name,client)
148 client:append("Bye-bye!")
149 h:broadcast("Shutting down.")
150 vlc.msg.info("Requested shutdown.")
151 vlc.misc.quit()
154 function quit(name,client)
155 if client.type == host.client_type.net then
156 logout(name,client)
157 else
158 shutdown(name,client)
162 function add(name,client,arg)
163 -- TODO: parse single and double quotes properly
164 local f
165 if name == "enqueue" then
166 f = vlc.playlist.enqueue
167 else
168 f = vlc.playlist.add
170 local options = {}
171 for o in string.gmatch(arg," +:([^ ]*)") do
172 table.insert(options,o)
174 arg = string.gsub(arg," +:.*$","")
175 f({{path=arg,options=options}})
178 function playlist_is_tree( client )
179 if client.env.flatplaylist == 0 then
180 return true
181 else
182 return false
186 function playlist(name,client,arg)
187 function playlist0(item,prefix)
188 local prefix = prefix or ""
189 if not item.flags.disabled then
190 local str = "| "..prefix..tostring(item.id).." - "..item.name
191 if item.duration > 0 then
192 str = str.." ("..common.durationtostring(item.duration)..")"
194 if item.nb_played > 0 then
195 str = str.." [played "..tostring(item.nb_played).." time"
196 if item.nb_played > 1 then
197 str = str .. "s"
199 str = str .. "]"
201 client:append(str)
203 if item.children then
204 for _, c in ipairs(item.children) do
205 playlist0(c,prefix.." ")
209 local playlist
210 local tree = playlist_is_tree(client)
211 if name == "search" then
212 playlist = vlc.playlist.search(arg or "", tree)
213 else
214 if tonumber(arg) then
215 playlist = vlc.playlist.get(tonumber(arg), tree)
216 elseif arg then
217 playlist = vlc.playlist.get(arg, tree)
218 else
219 playlist = vlc.playlist.get(nil, tree)
222 if name == "search" then
223 client:append("+----[ Search - "..(arg or "`reset'").." ]")
224 else
225 client:append("+----[ Playlist - "..playlist.name.." ]")
227 if playlist.children then
228 for _, item in ipairs(playlist.children) do
229 playlist0(item)
231 else
232 playlist0(playlist)
234 if name == "search" then
235 client:append("+----[ End of search - Use `search' to reset ]")
236 else
237 client:append("+----[ End of playlist ]")
241 function playlist_sort(name,client,arg)
242 if not arg then
243 client:append("Valid sort keys are: id, title, artist, genre, random, duration, album.")
244 else
245 local tree = playlist_is_tree(client)
246 vlc.playlist.sort(arg,false,tree)
250 function services_discovery(name,client,arg)
251 if arg then
252 if vlc.sd.is_loaded(arg) then
253 vlc.sd.remove(arg)
254 client:append(arg.." disabled.")
255 else
256 vlc.sd.add(arg)
257 client:append(arg.." enabled.")
259 else
260 local sd = vlc.sd.get_services_names()
261 client:append("+----[ Services discovery ]")
262 for n,ln in pairs(sd) do
263 local status
264 if vlc.sd.is_loaded(n) then
265 status = "enabled"
266 else
267 status = "disabled"
269 client:append("| "..n..": " .. ln .. " (" .. status .. ")")
271 client:append("+----[ End of services discovery ]")
275 function print_text(label,text)
276 return function(name,client)
277 client:append("+----[ "..label.." ]")
278 client:append "|"
279 for line in string.gmatch(text,".-\r?\n") do
280 client:append("| "..string.gsub(line,"\r?\n",""))
282 client:append "|"
283 client:append("+----[ End of "..string.lower(label).." ]")
287 function help(name,client,arg)
288 local width = client.env.width
289 local long = (name == "longhelp")
290 local extra = ""
291 if arg then extra = "matching `" .. arg .. "' " end
292 client:append("+----[ Remote control commands "..extra.."]")
293 for i, cmd in ipairs(commands_ordered) do
294 if (cmd == "" or not commands[cmd].adv or long)
295 and (not arg or string.match(cmd,arg)) then
296 local str = "| " .. cmd
297 if cmd ~= "" then
298 local val = commands[cmd]
299 if val.aliases then
300 for _,a in ipairs(val.aliases) do
301 str = str .. ", " .. a
304 if val.args then str = str .. " " .. val.args end
305 if #str%2 == 1 then str = str .. " " end
306 str = str .. string.rep(" .",(width-(#str+#val.help)-1)/2)
307 str = str .. string.rep(" ",width-#str-#val.help) .. val.help
309 client:append(str)
312 client:append("+----[ end of help ]")
315 function input_info(name,client)
316 local item = vlc.input.item()
317 if(item == nil) then return end
318 local categories = item:info()
319 for cat, infos in pairs(categories) do
320 client:append("+----[ "..cat.." ]")
321 client:append("|")
322 for name, value in pairs(infos) do
323 client:append("| "..name..": "..value)
325 client:append("|")
327 client:append("+----[ end of stream info ]")
330 function stats(name,client)
331 local item = vlc.input.item()
332 if(item == nil) then return end
333 local stats_tab = item:stats()
335 client:append("+----[ begin of statistical info")
336 client:append("+-[Incoming]")
337 client:append("| input bytes read : "..string.format("%8.0f KiB",stats_tab["read_bytes"]/1024))
338 client:append("| input bitrate : "..string.format("%6.0f kb/s",stats_tab["input_bitrate"]*8000))
339 client:append("| demux bytes read : "..string.format("%8.0f KiB",stats_tab["demux_read_bytes"]/1024))
340 client:append("| demux bitrate : "..string.format("%6.0f kb/s",stats_tab["demux_bitrate"]*8000))
341 client:append("| demux corrupted : "..string.format("%5i",stats_tab["demux_corrupted"]))
342 client:append("| discontinuities : "..string.format("%5i",stats_tab["demux_discontinuity"]))
343 client:append("|")
344 client:append("+-[Video Decoding]")
345 client:append("| video decoded : "..string.format("%5i",stats_tab["decoded_video"]))
346 client:append("| frames displayed : "..string.format("%5i",stats_tab["displayed_pictures"]))
347 client:append("| frames lost : "..string.format("%5i",stats_tab["lost_pictures"]))
348 client:append("|")
349 client:append("+-[Audio Decoding]")
350 client:append("| audio decoded : "..string.format("%5i",stats_tab["decoded_audio"]))
351 client:append("| buffers played : "..string.format("%5i",stats_tab["played_abuffers"]))
352 client:append("| buffers lost : "..string.format("%5i",stats_tab["lost_abuffers"]))
353 client:append("|")
354 client:append("+-[Streaming]")
355 client:append("| packets sent : "..string.format("%5i",stats_tab["sent_packets"]))
356 client:append("| bytes sent : "..string.format("%8.0f KiB",stats_tab["sent_bytes"]/1024))
357 client:append("| sending bitrate : "..string.format("%6.0f kb/s",stats_tab["send_bitrate"]*8000))
358 client:append("+----[ end of statistical info ]")
361 function playlist_status(name,client)
362 local item = vlc.input.item()
363 if(item ~= nil) then
364 client:append( "( new input: " .. vlc.strings.decode_uri(item:uri()) .. " )" )
366 client:append( "( audio volume: " .. tostring(vlc.volume.get()) .. " )")
367 client:append( "( state " .. vlc.playlist.status() .. " )")
370 function is_playing(name,client)
371 if vlc.input.is_playing() then client:append "1" else client:append "0" end
374 function get_title(name,client)
375 local item = vlc.input.item()
376 if item then
377 client:append(item:name())
378 else
379 client:append("")
383 function ret_print(foo,start,stop)
384 local start = start or ""
385 local stop = stop or ""
386 return function(discard,client,...) client:append(start..tostring(foo(...))..stop) end
389 function get_time(var)
390 return function(name,client)
391 local input = vlc.object.input()
392 client:append(math.floor(vlc.var.get( input, var )))
396 function titlechap(name,client,value)
397 local input = vlc.object.input()
398 local var = string.gsub( name, "_.*$", "" )
399 if value then
400 vlc.var.set( input, var, value )
401 else
402 local item = vlc.var.get( input, var )
403 -- Todo: add item name conversion
404 client:append(item)
407 function titlechap_offset(client,offset)
408 return function(name,value)
409 local input = vlc.object.input()
410 local var = string.gsub( name, "_.*$", "" )
411 vlc.var.set( input, var, vlc.var.get( input, var )+offset )
415 function seek(name,client,value)
416 common.seek(value)
419 function volume(name,client,value)
420 if value then
421 common.volume(value)
422 else
423 client:append(tostring(vlc.volume.get()))
427 function rate(name,client,value)
428 local input = vlc.object.input()
429 if name == "rate" then
430 vlc.var.set(input, "rate", tonumber(value))
431 elseif name == "normal" then
432 vlc.var.set(input,"rate",1)
433 else
434 vlc.var.set(input,"rate-"..name,nil)
438 function frame(name,client)
439 vlc.var.trigger_callback(vlc.object.input(),"frame-next");
442 function listvalue(obj,var)
443 return function(client,value)
444 local o = vlc.object.find(nil,obj,"anywhere")
445 if not o then return end
446 if value then
447 vlc.var.set( o, var, value )
448 else
449 local c = vlc.var.get( o, var )
450 local v, l = vlc.var.get_list( o, var )
451 client:append("+----[ "..var.." ]")
452 for i,val in ipairs(v) do
453 local mark = (val==c)and " *" or ""
454 client:append("| "..tostring(val).." - "..tostring(l[i])..mark)
456 client:append("+----[ end of "..var.." ]")
461 function menu(name,client,value)
462 local map = { on='show', off='hide', up='up', down='down', left='prev', right='next', ['select']='activate' }
463 if map[value] and vlc.osd.menu[map[value]] then
464 vlc.osd.menu[map[value]]()
465 else
466 client:append("Unknown menu command '"..tostring(value).."'")
470 function hotkey(name, client, value)
471 if not value then
472 client:append("Please specify a hotkey (ie key-quit or quit)")
473 elseif not common.hotkey(value) and not common.hotkey("key-"..value) then
474 client:append("Unknown hotkey '"..value.."'")
478 function eval(client,val)
479 client:append(tostring(loadstring("return "..val)()))
482 --[[ Declare commands, register their callback functions and provide
483 help strings here.
484 Syntax is:
485 "<command name>"; { func = <function>; [ args = "<str>"; ] help = "<str>"; [ adv = <bool>; ] [ aliases = { ["<str>";]* }; ] }
487 commands_ordered = {
488 { "add"; { func = add; args = "XYZ"; help = "add XYZ to playlist" } };
489 { "enqueue"; { func = add; args = "XYZ"; help = "queue XYZ to playlist" } };
490 { "playlist"; { func = playlist; help = "show items currently in playlist" } };
491 { "search"; { func = playlist; args = "[string]"; help = "search for items in playlist (or reset search)" } };
492 { "sort"; { func = playlist_sort; args = "key"; help = "sort the playlist" } };
493 { "sd"; { func = services_discovery; args = "[sd]"; help = "show services discovery or toggle" } };
494 { "play"; { func = skip2(vlc.playlist.play); help = "play stream" } };
495 { "stop"; { func = skip2(vlc.playlist.stop); help = "stop stream" } };
496 { "next"; { func = skip2(vlc.playlist.next); help = "next playlist item" } };
497 { "prev"; { func = skip2(vlc.playlist.prev); help = "previous playlist item" } };
498 { "goto"; { func = skip2(vlc.playlist.goto); help = "goto item at index" } };
499 { "repeat"; { func = skip2(vlc.playlist.repeat_); args = "[on|off]"; help = "toggle playlist repeat" } };
500 { "loop"; { func = skip2(vlc.playlist.loop); args = "[on|off]"; help = "toggle playlist loop" } };
501 { "random"; { func = skip2(vlc.playlist.random); args = "[on|off]"; help = "toggle playlist random" } };
502 { "clear"; { func = skip2(vlc.playlist.clear); help = "clear the playlist" } };
503 { "status"; { func = playlist_status; help = "current playlist status" } };
504 { "title"; { func = titlechap; args = "[X]"; help = "set/get title in current item" } };
505 { "title_n"; { func = titlechap_offset(1); help = "next title in current item" } };
506 { "title_p"; { func = titlechap_offset(-1); help = "previous title in current item" } };
507 { "chapter"; { func = titlechap; args = "[X]"; help = "set/get chapter in current item" } };
508 { "chapter_n"; { func = titlechap_offset(1); help = "next chapter in current item" } };
509 { "chapter_p"; { func = titlechap_offset(-1); help = "previous chapter in current item" } };
510 { "" };
511 { "seek"; { func = seek; args = "X"; help = "seek in seconds, for instance `seek 12'" } };
512 { "pause"; { func = skip2(vlc.playlist.pause); help = "toggle pause" } };
513 { "fastforward"; { func = setarg(common.hotkey,"key-jump+extrashort"); help = "set to maximum rate" } };
514 { "rewind"; { func = setarg(common.hotkey,"key-jump-extrashort"); help = "set to minimum rate" } };
515 { "faster"; { func = rate; help = "faster playing of stream" } };
516 { "slower"; { func = rate; help = "slower playing of stream" } };
517 { "normal"; { func = rate; help = "normal playing of stream" } };
518 { "rate"; { func = rate; args = "[playback rate]"; help = "set playback rate to value" } };
519 { "frame"; { func = frame; help = "play frame by frame" } };
520 { "fullscreen"; { func = skip2(vlc.video.fullscreen); args = "[on|off]"; help = "toggle fullscreen"; aliases = { "f", "F" } } };
521 { "info"; { func = input_info; help = "information about the current stream" } };
522 { "stats"; { func = stats; help = "show statistical information" } };
523 { "get_time"; { func = get_time("time"); help = "seconds elapsed since stream's beginning" } };
524 { "is_playing"; { func = is_playing; help = "1 if a stream plays, 0 otherwise" } };
525 { "get_title"; { func = get_title; help = "the title of the current stream" } };
526 { "get_length"; { func = get_time("length"); help = "the length of the current stream" } };
527 { "" };
528 { "volume"; { func = volume; args = "[X]"; help = "set/get audio volume" } };
529 { "volup"; { func = ret_print(vlc.volume.up,"( audio volume: "," )"); args = "[X]"; help = "raise audio volume X steps" } };
530 { "voldown"; { func = ret_print(vlc.volume.down,"( audio volume: "," )"); args = "[X]"; help = "lower audio volume X steps" } };
531 { "adev"; { func = skip(listvalue("aout","audio-device")); args = "[X]"; help = "set/get audio device" } };
532 { "achan"; { func = skip(listvalue("aout","audio-channels")); args = "[X]"; help = "set/get audio channels" } };
533 { "atrack"; { func = skip(listvalue("input","audio-es")); args = "[X]"; help = "set/get audio track" } };
534 { "vtrack"; { func = skip(listvalue("input","video-es")); args = "[X]"; help = "set/get video track" } };
535 { "vratio"; { func = skip(listvalue("vout","aspect-ratio")); args = "[X]"; help = "set/get video aspect ratio" } };
536 { "vcrop"; { func = skip(listvalue("vout","crop")); args = "[X]"; help = "set/get video crop"; aliases = { "crop" } } };
537 { "vzoom"; { func = skip(listvalue("vout","zoom")); args = "[X]"; help = "set/get video zoom"; aliases = { "zoom" } } };
538 { "snapshot"; { func = common.snapshot; help = "take video snapshot" } };
539 { "strack"; { func = skip(listvalue("input","spu-es")); args = "[X]"; help = "set/get subtitles track" } };
540 { "hotkey"; { func = hotkey; args = "[hotkey name]"; help = "simulate hotkey press"; adv = true; aliases = { "key" } } };
541 { "menu"; { func = menu; args = "[on|off|up|down|left|right|select]"; help = "use menu"; adv = true } };
542 { "" };
543 { "set"; { func = set_env; args = "[var [value]]"; help = "set/get env var"; adv = true } };
544 { "save_env"; { func = save_env; help = "save env vars (for future clients)"; adv = true } };
545 { "alias"; { func = skip(alias); args = "[cmd]"; help = "set/get command aliases"; adv = true } };
546 { "description"; { func = print_text("Description",description); help = "describe this module" } };
547 { "license"; { func = print_text("License message",vlc.misc.license()); help = "print VLC's license message"; adv = true } };
548 { "help"; { func = help; args = "[pattern]"; help = "a help message"; aliases = { "?" } } };
549 { "longhelp"; { func = help; args = "[pattern]"; help = "a longer help message" } };
550 { "logout"; { func = logout; help = "exit (if in a socket connection)" } };
551 { "quit"; { func = quit; help = "quit VLC (or logout if in a socket connection)" } };
552 { "shutdown"; { func = shutdown; help = "shutdown VLC" } };
555 if config.eval then
556 commands_ordered[#commands_ordered] = { "eval"; { func = skip(eval); help = "eval some lua (*debug*)"; adv =true } }
559 commands = {}
560 for i, cmd in ipairs( commands_ordered ) do
561 if #cmd == 2 then
562 commands[cmd[1]]=cmd[2]
563 if cmd[2].aliases then
564 for _,a in ipairs(cmd[2].aliases) do
565 commands[a]=cmd[1]
569 commands_ordered[i]=cmd[1]
571 --[[ From now on commands_ordered is a list of the different command names
572 and commands is a associative array indexed by the command name. ]]
574 -- Compute the column width used when printing a the autocompletion list
575 env.colwidth = 0
576 for c,_ in pairs(commands) do
577 if #c > env.colwidth then env.colwidth = #c end
579 env.coldwidth = env.colwidth + 1
581 -- Count unimplemented functions
583 local count = 0
584 local list = "("
585 for c,v in pairs(commands) do
586 if v.func == fixme then
587 count = count + 1
588 if count ~= 1 then
589 list = list..","
591 list = list..c
594 list = list..")"
595 if count ~= 0 and env.welcome and env.welcome ~= "" then
596 env.welcome = env.welcome .. "\r\nWarning: "..count.." functions are still unimplemented "..list.."."
600 --[[ Utils ]]
601 function split_input(input)
602 local input = strip(input)
603 local s = string.find(input," ")
604 if s then
605 return string.sub(input,0,s-1), strip(string.sub(input,s))
606 else
607 return input
611 function call_command(cmd,client,arg)
612 if type(commands[cmd]) == type("") then
613 cmd = commands[cmd]
615 local ok, msg
616 if arg ~= nil then
617 ok, msg = pcall( commands[cmd].func, cmd, client, arg )
618 else
619 ok, msg = pcall( commands[cmd].func, cmd, client )
621 if not ok then
622 local a = arg or ""
623 if a ~= "" then a = " " .. a end
624 client:append("Error in `"..cmd..a.."' ".. msg)
628 function call_libvlc_command(cmd,client,arg)
629 local ok, vlcerr, vlcmsg = pcall( vlc.var.libvlc_command, cmd, arg )
630 if not ok then
631 local a = arg or ""
632 if a ~= "" then a = " " .. a end
633 client:append("Error in `"..cmd..a.."' ".. vlcerr) -- when pcall fails, the 2nd arg is the error message.
635 return vlcerr
638 function call_object_command(cmd,client,arg)
639 local var, val = split_input(arg)
640 local ok, vlcmsg, vlcerr, vlcerrmsg = pcall( vlc.var.command, cmd, var, val )
641 if not ok then
642 client:append("Error in `"..cmd.." "..var.." "..val.."' ".. vlcmsg) -- when pcall fails the 2nd arg is the error message
644 if vlcmsg ~= "" then
645 client:append(vlcmsg)
647 return vlcerr
650 --[[ Setup host ]]
651 require("host")
652 h = host.host()
653 -- No auth
654 h.status_callbacks[host.status.password] = function(client)
655 client.env = common.table_copy( env )
656 if client.env.welcome ~= "" then
657 client:send( client.env.welcome .. "\r\n")
659 client:switch_status(host.status.read)
661 -- Print prompt when switching a client's status to `read'
662 h.status_callbacks[host.status.read] = function(client)
663 client:send( client.env.prompt )
666 h:listen( config.hosts or config.host or "*console" )
668 --[[ The main loop ]]
669 while not vlc.misc.should_die() do
670 local write, read = h:accept_and_select()
672 for _, client in pairs(write) do
673 local len = client:send()
674 client.buffer = string.sub(client.buffer,len+1)
675 if client.buffer == "" then client:switch_status(host.status.read) end
678 for _, client in pairs(read) do
679 local input = client:recv(1000)
680 local done = false
681 if string.match(input,"\n$") then
682 client.buffer = string.gsub(client.buffer..input,"\r?\n$","")
683 done = true
684 elseif client.buffer == ""
685 and ((client.type == host.client_type.stdio and input == "")
686 or (client.type == host.client_type.net and input == "\004")) then
687 -- Caught a ^D
688 client.buffer = "quit"
689 done = true
690 else
691 client.buffer = client.buffer .. input
693 if done then
694 local cmd,arg = split_input(client.buffer)
695 client.buffer = ""
696 client:switch_status(host.status.write)
697 if commands[cmd] then
698 call_command(cmd,client,arg)
699 elseif string.sub(cmd,0,1)=='@'
700 and call_object_command(string.sub(cmd,2,#cmd),client,arg) == 0 then
702 elseif client.type == host.client_type.stdio
703 and call_libvlc_command(cmd,client,arg) == 0 then
705 else
706 local choices = {}
707 if client.env.autocompletion ~= 0 then
708 for v,_ in common.pairs_sorted(commands) do
709 if string.sub(v,0,#cmd)==cmd then
710 table.insert(choices, v)
714 if #choices == 1 and client.env.autoalias ~= 0 then
715 -- client:append("Aliasing to \""..choices[1].."\".")
716 cmd = choices[1]
717 call_command(cmd,client,arg)
718 else
719 client:append("Unknown command `"..cmd.."'. Type `help' for help.")
720 if #choices ~= 0 then
721 client:append("Possible choices are:")
722 local cols = math.floor(client.env.width/(client.env.colwidth+1))
723 local fmt = "%-"..client.env.colwidth.."s"
724 for i = 1, #choices do
725 choices[i] = string.format(fmt,choices[i])
727 for i = 1, #choices, cols do
728 local j = i + cols - 1
729 if j > #choices then j = #choices end
730 client:append(" "..table.concat(choices," ",i,j))