url: remove vlc_UrlParse() option parameter
[vlc.git] / modules / control / oldrc.c
1 /*****************************************************************************
2  * oldrc.c : remote control stdin/stdout module for vlc
3  *****************************************************************************
4  * Copyright (C) 2004-2009 the VideoLAN team
5  * $Id$
6  *
7  * Author: Peter Surda <shurdeek@panorama.sth.ac.at>
8  *         Jean-Paul Saman <jpsaman #_at_# m2x _replaceWith#dot_ nl>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <errno.h>                                                 /* ENOMEM */
34 #include <signal.h>
35 #include <assert.h>
36 #include <math.h>
37
38 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
39 #include <vlc_common.h>
40 #include <vlc_plugin.h>
41 #include <vlc_interface.h>
42 #include <vlc_input.h>
43 #include <vlc_aout.h>
44 #include <vlc_vout.h>
45 #include <vlc_playlist.h>
46 #include <vlc_keys.h>
47
48 #include <sys/types.h>
49 #include <unistd.h>
50
51
52 #include <vlc_network.h>
53 #include <vlc_url.h>
54 #include <vlc_charset.h>
55
56 #if defined(PF_UNIX) && !defined(PF_LOCAL)
57 #    define PF_LOCAL PF_UNIX
58 #endif
59
60 #if defined(AF_LOCAL) && ! defined(_WIN32)
61 #    include <sys/un.h>
62 #endif
63
64 #define MAX_LINE_LENGTH 1024
65 #define STATUS_CHANGE "status change: "
66
67 /* input_state_e from <vlc_input.h> */
68 static const char *ppsz_input_state[] = {
69     [INIT_S] = N_("Initializing"),
70     [OPENING_S] = N_("Opening"),
71     [PLAYING_S] = N_("Play"),
72     [PAUSE_S] = N_("Pause"),
73     [END_S] = N_("End"),
74     [ERROR_S] = N_("Error"),
75 };
76
77 /*****************************************************************************
78  * Local prototypes
79  *****************************************************************************/
80 static int  Activate     ( vlc_object_t * );
81 static void Deactivate   ( vlc_object_t * );
82 static void *Run         ( void * );
83
84 static void Help         ( intf_thread_t * );
85 static void RegisterCallbacks( intf_thread_t * );
86
87 static bool ReadCommand( intf_thread_t *, char *, int * );
88
89 static input_item_t *parse_MRL( const char * );
90
91 static int  Input        ( vlc_object_t *, char const *,
92                            vlc_value_t, vlc_value_t, void * );
93 static int  Playlist     ( vlc_object_t *, char const *,
94                            vlc_value_t, vlc_value_t, void * );
95 static int  Quit         ( vlc_object_t *, char const *,
96                            vlc_value_t, vlc_value_t, void * );
97 static int  Intf         ( vlc_object_t *, char const *,
98                            vlc_value_t, vlc_value_t, void * );
99 static int  Volume       ( vlc_object_t *, char const *,
100                            vlc_value_t, vlc_value_t, void * );
101 static int  VolumeMove   ( vlc_object_t *, char const *,
102                            vlc_value_t, vlc_value_t, void * );
103 static int  VideoConfig  ( vlc_object_t *, char const *,
104                            vlc_value_t, vlc_value_t, void * );
105 static int  AudioDevice  ( vlc_object_t *, char const *,
106                            vlc_value_t, vlc_value_t, void * );
107 static int  AudioChannel ( vlc_object_t *, char const *,
108                            vlc_value_t, vlc_value_t, void * );
109 static int  Statistics   ( vlc_object_t *, char const *,
110                            vlc_value_t, vlc_value_t, void * );
111
112 static int updateStatistics( intf_thread_t *, input_item_t *);
113
114 /* Status Callbacks */
115 static int VolumeChanged( vlc_object_t *, char const *,
116                           vlc_value_t, vlc_value_t, void * );
117 static int InputEvent( vlc_object_t *, char const *,
118                        vlc_value_t, vlc_value_t, void * );
119
120 struct intf_sys_t
121 {
122     int *pi_socket_listen;
123     int i_socket;
124     char *psz_unix_path;
125     vlc_thread_t thread;
126
127     /* status changes */
128     vlc_mutex_t       status_lock;
129     int               i_last_state;
130     playlist_t        *p_playlist;
131     input_thread_t    *p_input;
132     bool              b_input_buffering;
133
134 #ifdef _WIN32
135     HANDLE hConsoleIn;
136     bool b_quiet;
137 #endif
138 };
139
140 VLC_FORMAT(2, 3)
141 static void msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
142 {
143     va_list args;
144     char fmt_eol[strlen (psz_fmt) + 3];
145
146     snprintf (fmt_eol, sizeof (fmt_eol), "%s\r\n", psz_fmt);
147     va_start( args, psz_fmt );
148
149     if( p_intf->p_sys->i_socket == -1 )
150         utf8_vfprintf( stdout, fmt_eol, args );
151     else
152         net_vaPrintf( p_intf, p_intf->p_sys->i_socket, fmt_eol, args );
153     va_end( args );
154 }
155 #define msg_rc( ... ) msg_rc( p_intf, __VA_ARGS__ )
156
157 /*****************************************************************************
158  * Module descriptor
159  *****************************************************************************/
160 #define POS_TEXT N_("Show stream position")
161 #define POS_LONGTEXT N_("Show the current position in seconds within the " \
162                         "stream from time to time." )
163
164 #define TTY_TEXT N_("Fake TTY")
165 #define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.")
166
167 #define UNIX_TEXT N_("UNIX socket command input")
168 #define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
169                          "stdin." )
170
171 #define HOST_TEXT N_("TCP command input")
172 #define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
173             "You can set the address and port the interface will bind to." )
174
175 #ifdef _WIN32
176 #define QUIET_TEXT N_("Do not open a DOS command box interface")
177 #define QUIET_LONGTEXT N_( \
178     "By default the rc interface plugin will start a DOS command box. " \
179     "Enabling the quiet mode will not bring this command box but can also " \
180     "be pretty annoying when you want to stop VLC and no video window is " \
181     "open." )
182 #include "intromsg.h"
183 #endif
184
185 vlc_module_begin ()
186     set_shortname( N_("RC"))
187     set_category( CAT_INTERFACE )
188     set_subcategory( SUBCAT_INTERFACE_MAIN )
189     set_description( N_("Remote control interface") )
190     add_bool( "rc-show-pos", false, POS_TEXT, POS_LONGTEXT, true )
191
192 #ifdef _WIN32
193     add_bool( "rc-quiet", false, QUIET_TEXT, QUIET_LONGTEXT, false )
194 #else
195 #if defined (HAVE_ISATTY)
196     add_bool( "rc-fake-tty", false, TTY_TEXT, TTY_LONGTEXT, true )
197 #endif
198     add_string( "rc-unix", NULL, UNIX_TEXT, UNIX_LONGTEXT, true )
199 #endif
200     add_string( "rc-host", NULL, HOST_TEXT, HOST_LONGTEXT, true )
201
202     set_capability( "interface", 20 )
203
204     set_callbacks( Activate, Deactivate )
205 #ifdef _WIN32
206     add_shortcut( "rc" )
207 #endif
208 vlc_module_end ()
209
210 /*****************************************************************************
211  * Activate: initialize and create stuff
212  *****************************************************************************/
213 static int Activate( vlc_object_t *p_this )
214 {
215     /* FIXME: This function is full of memory leaks and bugs in error paths. */
216     intf_thread_t *p_intf = (intf_thread_t*)p_this;
217     playlist_t *p_playlist = pl_Get( p_intf );
218     char *psz_host, *psz_unix_path = NULL;
219     int  *pi_socket = NULL;
220
221 #ifndef _WIN32
222 #if defined(HAVE_ISATTY)
223     /* Check that stdin is a TTY */
224     if( !var_InheritBool( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
225     {
226         msg_Warn( p_intf, "fd 0 is not a TTY" );
227         return VLC_EGENERIC;
228     }
229 #endif
230
231     psz_unix_path = var_InheritString( p_intf, "rc-unix" );
232     if( psz_unix_path )
233     {
234         int i_socket;
235
236 #ifndef AF_LOCAL
237         msg_Warn( p_intf, "your OS doesn't support filesystem sockets" );
238         free( psz_unix_path );
239         return VLC_EGENERIC;
240 #else
241         struct sockaddr_un addr;
242
243         memset( &addr, 0, sizeof(struct sockaddr_un) );
244
245         msg_Dbg( p_intf, "trying UNIX socket" );
246
247         if( (i_socket = vlc_socket( PF_LOCAL, SOCK_STREAM, 0, false ) ) < 0 )
248         {
249             msg_Warn( p_intf, "can't open socket: %s", vlc_strerror_c(errno) );
250             free( psz_unix_path );
251             return VLC_EGENERIC;
252         }
253
254         addr.sun_family = AF_LOCAL;
255         strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) );
256         addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0';
257
258         if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr))
259          && (errno == EADDRINUSE)
260          && connect (i_socket, (struct sockaddr *)&addr, sizeof (addr))
261          && (errno == ECONNREFUSED))
262         {
263             msg_Info (p_intf, "Removing dead UNIX socket: %s", psz_unix_path);
264             unlink (psz_unix_path);
265
266             if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr)))
267             {
268                 msg_Err (p_intf, "cannot bind UNIX socket at %s: %s",
269                          psz_unix_path, vlc_strerror_c(errno));
270                 free (psz_unix_path);
271                 net_Close (i_socket);
272                 return VLC_EGENERIC;
273             }
274         }
275
276         if( listen( i_socket, 1 ) )
277         {
278             msg_Warn (p_intf, "can't listen on socket: %s",
279                       vlc_strerror_c(errno));
280             free( psz_unix_path );
281             net_Close( i_socket );
282             return VLC_EGENERIC;
283         }
284
285         /* FIXME: we need a core function to merge listening sockets sets */
286         pi_socket = calloc( 2, sizeof( int ) );
287         if( pi_socket == NULL )
288         {
289             free( psz_unix_path );
290             net_Close( i_socket );
291             return VLC_ENOMEM;
292         }
293         pi_socket[0] = i_socket;
294         pi_socket[1] = -1;
295 #endif /* AF_LOCAL */
296     }
297 #endif /* !_WIN32 */
298
299     if( ( pi_socket == NULL ) &&
300         ( psz_host = var_InheritString( p_intf, "rc-host" ) ) != NULL )
301     {
302         vlc_url_t url;
303
304         vlc_UrlParse( &url, psz_host );
305
306         msg_Dbg( p_intf, "base: %s, port: %d", url.psz_host, url.i_port );
307
308         pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port);
309         if( pi_socket == NULL )
310         {
311             msg_Warn( p_intf, "can't listen to %s port %i",
312                       url.psz_host, url.i_port );
313             vlc_UrlClean( &url );
314             free( psz_host );
315             return VLC_EGENERIC;
316         }
317
318         vlc_UrlClean( &url );
319         free( psz_host );
320     }
321
322     intf_sys_t *p_sys = malloc( sizeof( *p_sys ) );
323     if( unlikely(p_sys == NULL) )
324     {
325         net_ListenClose( pi_socket );
326         free( psz_unix_path );
327         return VLC_ENOMEM;
328     }
329
330     p_intf->p_sys = p_sys;
331     p_sys->pi_socket_listen = pi_socket;
332     p_sys->i_socket = -1;
333     p_sys->psz_unix_path = psz_unix_path;
334     vlc_mutex_init( &p_sys->status_lock );
335     p_sys->i_last_state = PLAYLIST_STOPPED;
336     p_sys->b_input_buffering = false;
337     p_sys->p_playlist = p_playlist;
338     p_sys->p_input = NULL;
339
340     /* Non-buffered stdout */
341     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
342
343 #ifdef _WIN32
344     p_sys->b_quiet = var_InheritBool( p_intf, "rc-quiet" );
345 # if !VLC_WINSTORE_APP
346     if( !p_sys->b_quiet )
347         intf_consoleIntroMsg( p_intf );
348 # endif
349 #endif
350
351     if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
352         abort();
353
354     msg_rc( "%s", _("Remote control interface initialized. Type `help' for help.") );
355
356     /* Listen to audio volume updates */
357     var_AddCallback( p_sys->p_playlist, "volume", VolumeChanged, p_intf );
358     return VLC_SUCCESS;
359 }
360
361 /*****************************************************************************
362  * Deactivate: uninitialize and cleanup
363  *****************************************************************************/
364 static void Deactivate( vlc_object_t *p_this )
365 {
366     intf_thread_t *p_intf = (intf_thread_t*)p_this;
367     intf_sys_t *p_sys = p_intf->p_sys;
368
369     vlc_cancel( p_sys->thread );
370     var_DelCallback( p_sys->p_playlist, "volume", VolumeChanged, p_intf );
371     vlc_join( p_sys->thread, NULL );
372
373     if( p_sys->p_input != NULL )
374     {
375         var_DelCallback( p_sys->p_input, "intf-event", InputEvent, p_intf );
376         vlc_object_release( p_sys->p_input );
377     }
378
379     net_ListenClose( p_sys->pi_socket_listen );
380     if( p_sys->i_socket != -1 )
381         net_Close( p_sys->i_socket );
382     if( p_sys->psz_unix_path != NULL )
383     {
384 #if defined(AF_LOCAL) && !defined(_WIN32)
385         unlink( p_sys->psz_unix_path );
386 #endif
387         free( p_sys->psz_unix_path );
388     }
389     vlc_mutex_destroy( &p_sys->status_lock );
390     free( p_sys );
391 }
392
393 /*****************************************************************************
394  * RegisterCallbacks: Register callbacks to dynamic variables
395  *****************************************************************************/
396 static void RegisterCallbacks( intf_thread_t *p_intf )
397 {
398     /* Register commands that will be cleaned up upon object destruction */
399 #define ADD( name, type, target )                                   \
400     var_Create( p_intf, name, VLC_VAR_ ## type | VLC_VAR_ISCOMMAND ); \
401     var_AddCallback( p_intf, name, target, NULL );
402     ADD( "quit", VOID, Quit )
403     ADD( "intf", STRING, Intf )
404
405     ADD( "add", STRING, Playlist )
406     ADD( "repeat", STRING, Playlist )
407     ADD( "loop", STRING, Playlist )
408     ADD( "random", STRING, Playlist )
409     ADD( "enqueue", STRING, Playlist )
410     ADD( "playlist", VOID, Playlist )
411     ADD( "sort", VOID, Playlist )
412     ADD( "play", VOID, Playlist )
413     ADD( "stop", VOID, Playlist )
414     ADD( "clear", VOID, Playlist )
415     ADD( "prev", VOID, Playlist )
416     ADD( "next", VOID, Playlist )
417     ADD( "goto", INTEGER, Playlist )
418     ADD( "status", INTEGER, Playlist )
419
420     /* DVD commands */
421     ADD( "pause", VOID, Input )
422     ADD( "seek", INTEGER, Input )
423     ADD( "title", STRING, Input )
424     ADD( "title_n", VOID, Input )
425     ADD( "title_p", VOID, Input )
426     ADD( "chapter", STRING, Input )
427     ADD( "chapter_n", VOID, Input )
428     ADD( "chapter_p", VOID, Input )
429
430     ADD( "fastforward", VOID, Input )
431     ADD( "rewind", VOID, Input )
432     ADD( "faster", VOID, Input )
433     ADD( "slower", VOID, Input )
434     ADD( "normal", VOID, Input )
435     ADD( "frame", VOID, Input )
436
437     ADD( "atrack", STRING, Input )
438     ADD( "vtrack", STRING, Input )
439     ADD( "strack", STRING, Input )
440
441     /* video commands */
442     ADD( "vratio", STRING, VideoConfig )
443     ADD( "vcrop", STRING, VideoConfig )
444     ADD( "vzoom", STRING, VideoConfig )
445     ADD( "snapshot", VOID, VideoConfig )
446
447     /* audio commands */
448     ADD( "volume", STRING, Volume )
449     ADD( "volup", STRING, VolumeMove )
450     ADD( "voldown", STRING, VolumeMove )
451     ADD( "adev", STRING, AudioDevice )
452     ADD( "achan", STRING, AudioChannel )
453
454     /* misc menu commands */
455     ADD( "stats", BOOL, Statistics )
456
457 #undef ADD
458 }
459
460 /*****************************************************************************
461  * Run: rc thread
462  *****************************************************************************
463  * This part of the interface is in a separate thread so that we can call
464  * exec() from within it without annoying the rest of the program.
465  *****************************************************************************/
466 static void *Run( void *data )
467 {
468     intf_thread_t *p_intf = data;
469     intf_sys_t *p_sys = p_intf->p_sys;
470
471     char p_buffer[ MAX_LINE_LENGTH + 1 ];
472     bool b_showpos = var_InheritBool( p_intf, "rc-show-pos" );
473
474     int  i_size = 0;
475     int  i_oldpos = 0;
476     int  i_newpos;
477     int  canc = vlc_savecancel( );
478
479     p_buffer[0] = 0;
480
481 #ifdef _WIN32
482     /* Get the file descriptor of the console input */
483     p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
484     if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
485     {
486         msg_Err( p_intf, "couldn't find user input handle" );
487         return NULL;
488     }
489 #endif
490
491     /* Register commands that will be cleaned up upon object destruction */
492     RegisterCallbacks( p_intf );
493
494     /* status callbacks */
495
496     for( ;; )
497     {
498         char *psz_cmd, *psz_arg;
499         bool b_complete;
500
501         vlc_restorecancel( canc );
502
503         if( p_sys->pi_socket_listen != NULL && p_sys->i_socket == -1 )
504         {
505             p_sys->i_socket =
506                 net_Accept( p_intf, p_sys->pi_socket_listen );
507             if( p_sys->i_socket == -1 ) continue;
508         }
509
510         b_complete = ReadCommand( p_intf, p_buffer, &i_size );
511         canc = vlc_savecancel( );
512
513         /* Manage the input part */
514         if( p_sys->p_input == NULL )
515         {
516             p_sys->p_input = playlist_CurrentInput( p_sys->p_playlist );
517             /* New input has been registered */
518             if( p_sys->p_input )
519             {
520                 char *psz_uri = input_item_GetURI( input_GetItem( p_sys->p_input ) );
521                 msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
522                 free( psz_uri );
523
524                 var_AddCallback( p_sys->p_input, "intf-event", InputEvent, p_intf );
525             }
526         }
527
528         int state;
529         if( p_sys->p_input != NULL
530          && ((state = var_GetInteger( p_sys->p_input, "state")) == ERROR_S
531           || state == END_S) )
532         {
533             var_DelCallback( p_sys->p_input, "intf-event", InputEvent, p_intf );
534             vlc_object_release( p_sys->p_input );
535             p_sys->p_input = NULL;
536
537             p_sys->i_last_state = PLAYLIST_STOPPED;
538             msg_rc( STATUS_CHANGE "( stop state: 0 )" );
539         }
540
541         if( p_sys->p_input != NULL )
542         {
543             playlist_t *p_playlist = p_sys->p_playlist;
544
545             PL_LOCK;
546             int status = playlist_Status( p_playlist );
547             PL_UNLOCK;
548
549             if( p_sys->i_last_state != status )
550             {
551                 if( status == PLAYLIST_STOPPED )
552                 {
553                     p_sys->i_last_state = PLAYLIST_STOPPED;
554                     msg_rc( STATUS_CHANGE "( stop state: 5 )" );
555                 }
556                 else if( status == PLAYLIST_RUNNING )
557                 {
558                     p_sys->i_last_state = PLAYLIST_RUNNING;
559                     msg_rc( STATUS_CHANGE "( play state: 3 )" );
560                 }
561                 else if( status == PLAYLIST_PAUSED )
562                 {
563                     p_sys->i_last_state = PLAYLIST_PAUSED;
564                     msg_rc( STATUS_CHANGE "( pause state: 4 )" );
565                 }
566             }
567         }
568
569         if( p_sys->p_input && b_showpos )
570         {
571             i_newpos = 100 * var_GetFloat( p_sys->p_input, "position" );
572             if( i_oldpos != i_newpos )
573             {
574                 i_oldpos = i_newpos;
575                 msg_rc( "pos: %d%%", i_newpos );
576             }
577         }
578
579         /* Is there something to do? */
580         if( !b_complete ) continue;
581
582         /* Skip heading spaces */
583         psz_cmd = p_buffer;
584         while( *psz_cmd == ' ' )
585         {
586             psz_cmd++;
587         }
588
589         /* Split psz_cmd at the first space and make sure that
590          * psz_arg is valid */
591         psz_arg = strchr( psz_cmd, ' ' );
592         if( psz_arg )
593         {
594             *psz_arg++ = 0;
595             while( *psz_arg == ' ' )
596             {
597                 psz_arg++;
598             }
599         }
600         else
601         {
602             psz_arg = (char*)"";
603         }
604
605         /* If the user typed a registered local command, try it */
606         if( var_Type( p_intf, psz_cmd ) & VLC_VAR_ISCOMMAND )
607         {
608             vlc_value_t val;
609             int i_ret;
610             val.psz_string = psz_arg;
611
612             if ((var_Type( p_intf, psz_cmd) & VLC_VAR_CLASS) == VLC_VAR_VOID)
613                 i_ret = var_TriggerCallback( p_intf, psz_cmd );
614             else
615                 i_ret = var_Set( p_intf, psz_cmd, val );
616             msg_rc( "%s: returned %i (%s)",
617                     psz_cmd, i_ret, vlc_error( i_ret ) );
618         }
619         /* Or maybe it's a global command */
620         else if( var_Type( p_intf->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
621         {
622             vlc_value_t val;
623             int i_ret;
624
625             val.psz_string = psz_arg;
626             /* FIXME: it's a global command, but we should pass the
627              * local object as an argument, not p_intf->p_libvlc. */
628             if ((var_Type( p_intf->p_libvlc, psz_cmd) & VLC_VAR_CLASS) == VLC_VAR_VOID)
629                 i_ret = var_TriggerCallback( p_intf, psz_cmd );
630             else
631                 i_ret = var_Set( p_intf->p_libvlc, psz_cmd, val );
632             if( i_ret != 0 )
633             {
634                 msg_rc( "%s: returned %i (%s)",
635                          psz_cmd, i_ret, vlc_error( i_ret ) );
636             }
637         }
638         else if( !strcmp( psz_cmd, "logout" ) )
639         {
640             /* Close connection */
641             if( p_sys->i_socket != -1 )
642             {
643                 net_Close( p_sys->i_socket );
644                 p_sys->i_socket = -1;
645             }
646         }
647         else if( !strcmp( psz_cmd, "info" ) )
648         {
649             if( p_sys->p_input )
650             {
651                 int i, j;
652                 vlc_mutex_lock( &input_GetItem(p_sys->p_input)->lock );
653                 for ( i = 0; i < input_GetItem(p_sys->p_input)->i_categories; i++ )
654                 {
655                     info_category_t *p_category = input_GetItem(p_sys->p_input)
656                                                         ->pp_categories[i];
657
658                     msg_rc( "+----[ %s ]", p_category->psz_name );
659                     msg_rc( "| " );
660                     for ( j = 0; j < p_category->i_infos; j++ )
661                     {
662                         info_t *p_info = p_category->pp_infos[j];
663                         msg_rc( "| %s: %s", p_info->psz_name,
664                                 p_info->psz_value );
665                     }
666                     msg_rc( "| " );
667                 }
668                 msg_rc( "+----[ end of stream info ]" );
669                 vlc_mutex_unlock( &input_GetItem(p_sys->p_input)->lock );
670             }
671             else
672             {
673                 msg_rc( "no input" );
674             }
675         }
676         else if( !strcmp( psz_cmd, "is_playing" ) )
677         {
678             if( p_sys->p_input == NULL )
679             {
680                 msg_rc( "0" );
681             }
682             else
683             {
684                 msg_rc( "1" );
685             }
686         }
687         else if( !strcmp( psz_cmd, "get_time" ) )
688         {
689             int64_t t = 0;
690             if( p_sys->p_input != NULL )
691                 t = var_GetInteger( p_sys->p_input, "time" ) / CLOCK_FREQ;
692             msg_rc( "%"PRIu64, t );
693         }
694         else if( !strcmp( psz_cmd, "get_length" ) )
695         {
696             int64_t l = 0;
697             if( p_sys->p_input != NULL )
698                 l = var_GetInteger( p_sys->p_input, "length" ) / CLOCK_FREQ;
699             msg_rc( "%"PRIu64, l );
700         }
701         else if( !strcmp( psz_cmd, "get_title" ) )
702         {
703             if( p_sys->p_input == NULL )
704             {
705                 msg_rc("%s", "");
706             }
707             else
708             {
709                 msg_rc( "%s", input_GetItem(p_sys->p_input)->psz_name );
710             }
711         }
712         else if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "h", 1 )
713                  || !strncmp( psz_cmd, "H", 1 ) || !strncmp( psz_cmd, "?", 1 ) )
714         {
715             Help( p_intf );
716         }
717         else if( !strcmp( psz_cmd, "key" ) || !strcmp( psz_cmd, "hotkey" ) )
718         {
719             var_SetInteger( p_intf->p_libvlc, "key-action",
720                             vlc_GetActionId( psz_arg ) );
721         }
722         else switch( psz_cmd[0] )
723         {
724         case 'f':
725         case 'F':
726         {
727             bool fs;
728
729             if( !strncasecmp( psz_arg, "on", 2 ) )
730                 var_SetBool( p_sys->p_playlist, "fullscreen", fs = true );
731             else if( !strncasecmp( psz_arg, "off", 3 ) )
732                 var_SetBool( p_sys->p_playlist, "fullscreen", fs = false );
733             else
734                 fs = var_ToggleBool( p_sys->p_playlist, "fullscreen" );
735
736             if( p_sys->p_input != NULL )
737             {
738                 vout_thread_t *p_vout = input_GetVout( p_sys->p_input );
739                 if( p_vout )
740                 {
741                     var_SetBool( p_vout, "fullscreen", fs );
742                     vlc_object_release( p_vout );
743                 }
744             }
745             break;
746         }
747         case 's':
748         case 'S':
749             ;
750             break;
751
752         case '\0':
753             /* Ignore empty lines */
754             break;
755
756         default:
757             msg_rc(_("Unknown command `%s'. Type `help' for help."), psz_cmd);
758             break;
759         }
760
761         /* Command processed */
762         i_size = 0; p_buffer[0] = 0;
763     }
764
765     msg_rc( STATUS_CHANGE "( stop state: 0 )" );
766     msg_rc( STATUS_CHANGE "( quit )" );
767
768     vlc_restorecancel( canc );
769
770     return NULL;
771 }
772
773 static void Help( intf_thread_t *p_intf)
774 {
775     msg_rc("%s", _("+----[ Remote control commands ]"));
776     msg_rc(  "| ");
777     msg_rc("%s", _("| add XYZ  . . . . . . . . . . . . add XYZ to playlist"));
778     msg_rc("%s", _("| enqueue XYZ  . . . . . . . . . queue XYZ to playlist"));
779     msg_rc("%s", _("| playlist . . . . .  show items currently in playlist"));
780     msg_rc("%s", _("| play . . . . . . . . . . . . . . . . . . play stream"));
781     msg_rc("%s", _("| stop . . . . . . . . . . . . . . . . . . stop stream"));
782     msg_rc("%s", _("| next . . . . . . . . . . . . . .  next playlist item"));
783     msg_rc("%s", _("| prev . . . . . . . . . . . .  previous playlist item"));
784     msg_rc("%s", _("| goto . . . . . . . . . . . . . .  goto item at index"));
785     msg_rc("%s", _("| repeat [on|off] . . . .  toggle playlist item repeat"));
786     msg_rc("%s", _("| loop [on|off] . . . . . . . . . toggle playlist loop"));
787     msg_rc("%s", _("| random [on|off] . . . . . . .  toggle random jumping"));
788     msg_rc("%s", _("| clear . . . . . . . . . . . . . . clear the playlist"));
789     msg_rc("%s", _("| status . . . . . . . . . . . current playlist status"));
790     msg_rc("%s", _("| title [X]  . . . . . . set/get title in current item"));
791     msg_rc("%s", _("| title_n  . . . . . . . .  next title in current item"));
792     msg_rc("%s", _("| title_p  . . . . . .  previous title in current item"));
793     msg_rc("%s", _("| chapter [X]  . . . . set/get chapter in current item"));
794     msg_rc("%s", _("| chapter_n  . . . . . .  next chapter in current item"));
795     msg_rc("%s", _("| chapter_p  . . . .  previous chapter in current item"));
796     msg_rc(  "| ");
797     msg_rc("%s", _("| seek X . . . seek in seconds, for instance `seek 12'"));
798     msg_rc("%s", _("| pause  . . . . . . . . . . . . . . . .  toggle pause"));
799     msg_rc("%s", _("| fastforward  . . . . . . . .  .  set to maximum rate"));
800     msg_rc("%s", _("| rewind  . . . . . . . . . . . .  set to minimum rate"));
801     msg_rc("%s", _("| faster . . . . . . . . . .  faster playing of stream"));
802     msg_rc("%s", _("| slower . . . . . . . . . .  slower playing of stream"));
803     msg_rc("%s", _("| normal . . . . . . . . . .  normal playing of stream"));
804     msg_rc("%s", _("| frame. . . . . . . . . .  play frame by frame"));
805     msg_rc("%s", _("| f [on|off] . . . . . . . . . . . . toggle fullscreen"));
806     msg_rc("%s", _("| info . . . . .  information about the current stream"));
807     msg_rc("%s", _("| stats  . . . . . . . .  show statistical information"));
808     msg_rc("%s", _("| get_time . . seconds elapsed since stream's beginning"));
809     msg_rc("%s", _("| is_playing . . . .  1 if a stream plays, 0 otherwise"));
810     msg_rc("%s", _("| get_title . . . . .  the title of the current stream"));
811     msg_rc("%s", _("| get_length . . . .  the length of the current stream"));
812     msg_rc(  "| ");
813     msg_rc("%s", _("| volume [X] . . . . . . . . . .  set/get audio volume"));
814     msg_rc("%s", _("| volup [X]  . . . . . . .  raise audio volume X steps"));
815     msg_rc("%s", _("| voldown [X]  . . . . . .  lower audio volume X steps"));
816     msg_rc("%s", _("| adev [device]  . . . . . . . .  set/get audio device"));
817     msg_rc("%s", _("| achan [X]. . . . . . . . . .  set/get audio channels"));
818     msg_rc("%s", _("| atrack [X] . . . . . . . . . . . set/get audio track"));
819     msg_rc("%s", _("| vtrack [X] . . . . . . . . . . . set/get video track"));
820     msg_rc("%s", _("| vratio [X]  . . . . . . . set/get video aspect ratio"));
821     msg_rc("%s", _("| vcrop [X]  . . . . . . . . . . .  set/get video crop"));
822     msg_rc("%s", _("| vzoom [X]  . . . . . . . . . . .  set/get video zoom"));
823     msg_rc("%s", _("| snapshot . . . . . . . . . . . . take video snapshot"));
824     msg_rc("%s", _("| strack [X] . . . . . . . . .  set/get subtitle track"));
825     msg_rc("%s", _("| key [hotkey name] . . . . . .  simulate hotkey press"));
826     msg_rc(  "| ");
827     msg_rc("%s", _("| help . . . . . . . . . . . . . . . this help message"));
828     msg_rc("%s", _("| logout . . . . . . .  exit (if in socket connection)"));
829     msg_rc("%s", _("| quit . . . . . . . . . . . . . . . . . . .  quit vlc"));
830     msg_rc(  "| ");
831     msg_rc("%s", _("+----[ end of help ]"));
832 }
833
834 /********************************************************************
835  * Status callback routines
836  ********************************************************************/
837 static int VolumeChanged( vlc_object_t *p_this, char const *psz_cmd,
838     vlc_value_t oldval, vlc_value_t newval, void *p_data )
839 {
840     (void) p_this;
841     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval);
842     intf_thread_t *p_intf = (intf_thread_t*)p_data;
843
844     vlc_mutex_lock( &p_intf->p_sys->status_lock );
845     msg_rc( STATUS_CHANGE "( audio volume: %ld )",
846             lroundf(newval.f_float * AOUT_VOLUME_DEFAULT) );
847     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
848     return VLC_SUCCESS;
849 }
850
851 static void StateChanged( intf_thread_t *p_intf, input_thread_t *p_input )
852 {
853     playlist_t *p_playlist = p_intf->p_sys->p_playlist;
854
855     PL_LOCK;
856     const int i_status = playlist_Status( p_playlist );
857     PL_UNLOCK;
858
859     /* */
860     const char *psz_cmd;
861     switch( i_status )
862     {
863     case PLAYLIST_STOPPED:
864         psz_cmd = "stop";
865         break;
866     case PLAYLIST_RUNNING:
867         psz_cmd = "play";
868         break;
869     case PLAYLIST_PAUSED:
870         psz_cmd = "pause";
871         break;
872     default:
873         psz_cmd = "";
874         break;
875     }
876
877     /* */
878     const int i_state = var_GetInteger( p_input, "state" );
879
880     vlc_mutex_lock( &p_intf->p_sys->status_lock );
881     msg_rc( STATUS_CHANGE "( %s state: %d ): %s", psz_cmd,
882             i_state, ppsz_input_state[i_state] );
883     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
884 }
885 static void RateChanged( intf_thread_t *p_intf,
886                          input_thread_t *p_input )
887 {
888     vlc_mutex_lock( &p_intf->p_sys->status_lock );
889     msg_rc( STATUS_CHANGE "( new rate: %.3f )",
890             var_GetFloat( p_input, "rate" ) );
891     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
892 }
893 static void PositionChanged( intf_thread_t *p_intf,
894                              input_thread_t *p_input )
895 {
896     vlc_mutex_lock( &p_intf->p_sys->status_lock );
897     if( p_intf->p_sys->b_input_buffering )
898         msg_rc( STATUS_CHANGE "( time: %"PRId64"s )",
899                 (var_GetInteger( p_input, "time" ) / CLOCK_FREQ) );
900     p_intf->p_sys->b_input_buffering = false;
901     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
902 }
903 static void CacheChanged( intf_thread_t *p_intf )
904 {
905     vlc_mutex_lock( &p_intf->p_sys->status_lock );
906     p_intf->p_sys->b_input_buffering = true;
907     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
908 }
909
910 static int InputEvent( vlc_object_t *p_this, char const *psz_cmd,
911                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
912 {
913     VLC_UNUSED(psz_cmd);
914     VLC_UNUSED(oldval);
915     input_thread_t *p_input = (input_thread_t*)p_this;
916     intf_thread_t *p_intf = p_data;
917
918     switch( newval.i_int )
919     {
920     case INPUT_EVENT_STATE:
921     case INPUT_EVENT_DEAD:
922         StateChanged( p_intf, p_input );
923         break;
924     case INPUT_EVENT_RATE:
925         RateChanged( p_intf, p_input );
926         break;
927     case INPUT_EVENT_POSITION:
928         PositionChanged( p_intf, p_input );
929         break;
930     case INPUT_EVENT_CACHE:
931         CacheChanged( p_intf );
932         break;
933     default:
934         break;
935     }
936     return VLC_SUCCESS;
937 }
938
939 /********************************************************************
940  * Command routines
941  ********************************************************************/
942 static int Input( vlc_object_t *p_this, char const *psz_cmd,
943                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
944 {
945     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
946     intf_thread_t *p_intf = (intf_thread_t*)p_this;
947     input_thread_t *p_input =
948         playlist_CurrentInput( p_intf->p_sys->p_playlist );
949     int i_error = VLC_EGENERIC;
950
951     if( !p_input )
952         return VLC_ENOOBJ;
953
954     int state = var_GetInteger( p_input, "state" );
955     if( ( state == PAUSE_S ) &&
956         ( strcmp( psz_cmd, "pause" ) != 0 ) && (strcmp( psz_cmd,"frame") != 0 ) )
957     {
958         msg_rc( "%s", _("Press pause to continue.") );
959     }
960     else
961     /* Parse commands that only require an input */
962     if( !strcmp( psz_cmd, "pause" ) )
963     {
964         playlist_TogglePause( p_intf->p_sys->p_playlist );
965         i_error = VLC_SUCCESS;
966     }
967     else if( !strcmp( psz_cmd, "seek" ) )
968     {
969         if( strlen( newval.psz_string ) > 0 &&
970             newval.psz_string[strlen( newval.psz_string ) - 1] == '%' )
971         {
972             float f = atof( newval.psz_string ) / 100.0;
973             var_SetFloat( p_input, "position", f );
974         }
975         else
976         {
977             mtime_t t = atoi( newval.psz_string );
978             var_SetInteger( p_input, "time", CLOCK_FREQ * t );
979         }
980         i_error = VLC_SUCCESS;
981     }
982     else if ( !strcmp( psz_cmd, "fastforward" ) )
983     {
984         if( var_GetBool( p_input, "can-rate" ) )
985         {
986             float f_rate = var_GetFloat( p_input, "rate" );
987             f_rate = (f_rate < 0) ? -f_rate : f_rate * 2;
988             var_SetFloat( p_input, "rate", f_rate );
989         }
990         else
991         {
992             var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_JUMP_FORWARD_EXTRASHORT );
993         }
994         i_error = VLC_SUCCESS;
995     }
996     else if ( !strcmp( psz_cmd, "rewind" ) )
997     {
998         if( var_GetBool( p_input, "can-rewind" ) )
999         {
1000             float f_rate = var_GetFloat( p_input, "rate" );
1001             f_rate = (f_rate > 0) ? -f_rate : f_rate * 2;
1002             var_SetFloat( p_input, "rate", f_rate );
1003         }
1004         else
1005         {
1006             var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_JUMP_BACKWARD_EXTRASHORT );
1007         }
1008         i_error = VLC_SUCCESS;
1009     }
1010     else if ( !strcmp( psz_cmd, "faster" ) )
1011     {
1012         var_TriggerCallback( p_intf->p_sys->p_playlist, "rate-faster" );
1013         i_error = VLC_SUCCESS;
1014     }
1015     else if ( !strcmp( psz_cmd, "slower" ) )
1016     {
1017         var_TriggerCallback( p_intf->p_sys->p_playlist, "rate-slower" );
1018         i_error = VLC_SUCCESS;
1019     }
1020     else if ( !strcmp( psz_cmd, "normal" ) )
1021     {
1022         var_SetFloat( p_intf->p_sys->p_playlist, "rate", 1. );
1023         i_error = VLC_SUCCESS;
1024     }
1025     else if ( !strcmp( psz_cmd, "frame" ) )
1026     {
1027         var_TriggerCallback( p_input, "frame-next" );
1028         i_error = VLC_SUCCESS;
1029     }
1030     else if( !strcmp( psz_cmd, "chapter" ) ||
1031              !strcmp( psz_cmd, "chapter_n" ) ||
1032              !strcmp( psz_cmd, "chapter_p" ) )
1033     {
1034         if( !strcmp( psz_cmd, "chapter" ) )
1035         {
1036             if ( *newval.psz_string )
1037             {
1038                 /* Set. */
1039                 var_SetInteger( p_input, "chapter", atoi( newval.psz_string ) );
1040             }
1041             else
1042             {
1043                 /* Get. */
1044                 int i_chap = var_GetInteger( p_input, "chapter" );
1045                 int i_chapter_count = var_CountChoices( p_input, "chapter" );
1046                 msg_rc( "Currently playing chapter %d/%d.", i_chap,
1047                         i_chapter_count );
1048             }
1049         }
1050         else if( !strcmp( psz_cmd, "chapter_n" ) )
1051             var_TriggerCallback( p_input, "next-chapter" );
1052         else if( !strcmp( psz_cmd, "chapter_p" ) )
1053             var_TriggerCallback( p_input, "prev-chapter" );
1054         i_error = VLC_SUCCESS;
1055     }
1056     else if( !strcmp( psz_cmd, "title" ) ||
1057              !strcmp( psz_cmd, "title_n" ) ||
1058              !strcmp( psz_cmd, "title_p" ) )
1059     {
1060         if( !strcmp( psz_cmd, "title" ) )
1061         {
1062             if ( *newval.psz_string )
1063                 /* Set. */
1064                 var_SetInteger( p_input, "title", atoi( newval.psz_string ) );
1065             else
1066             {
1067                 /* Get. */
1068                 int i_title = var_GetInteger( p_input, "title" );
1069                 int i_title_count = var_CountChoices( p_input, "title" );
1070                 msg_rc( "Currently playing title %d/%d.", i_title,
1071                         i_title_count );
1072             }
1073         }
1074         else if( !strcmp( psz_cmd, "title_n" ) )
1075             var_TriggerCallback( p_input, "next-title" );
1076         else if( !strcmp( psz_cmd, "title_p" ) )
1077             var_TriggerCallback( p_input, "prev-title" );
1078
1079         i_error = VLC_SUCCESS;
1080     }
1081     else if(    !strcmp( psz_cmd, "atrack" )
1082              || !strcmp( psz_cmd, "vtrack" )
1083              || !strcmp( psz_cmd, "strack" ) )
1084     {
1085         const char *psz_variable;
1086         vlc_value_t val_name;
1087
1088         if( !strcmp( psz_cmd, "atrack" ) )
1089         {
1090             psz_variable = "audio-es";
1091         }
1092         else if( !strcmp( psz_cmd, "vtrack" ) )
1093         {
1094             psz_variable = "video-es";
1095         }
1096         else
1097         {
1098             psz_variable = "spu-es";
1099         }
1100
1101         /* Get the descriptive name of the variable */
1102         var_Change( p_input, psz_variable, VLC_VAR_GETTEXT,
1103                      &val_name, NULL );
1104         if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1105
1106         if( newval.psz_string && *newval.psz_string )
1107         {
1108             /* set */
1109             i_error = var_SetInteger( p_input, psz_variable,
1110                                       atoi( newval.psz_string ) );
1111         }
1112         else
1113         {
1114             /* get */
1115             vlc_value_t val, text;
1116             int i, i_value;
1117
1118             if ( var_Get( p_input, psz_variable, &val ) < 0 )
1119                 goto out;
1120             i_value = val.i_int;
1121
1122             if ( var_Change( p_input, psz_variable,
1123                              VLC_VAR_GETCHOICES, &val, &text ) < 0 )
1124                 goto out;
1125
1126             msg_rc( "+----[ %s ]", val_name.psz_string );
1127             for ( i = 0; i < val.p_list->i_count; i++ )
1128             {
1129                 if ( i_value == val.p_list->p_values[i].i_int )
1130                     msg_rc( "| %"PRId64" - %s *",
1131                             val.p_list->p_values[i].i_int,
1132                             text.p_list->p_values[i].psz_string );
1133                 else
1134                     msg_rc( "| %"PRId64" - %s",
1135                             val.p_list->p_values[i].i_int,
1136                             text.p_list->p_values[i].psz_string );
1137             }
1138             var_FreeList( &val, &text );
1139             msg_rc( "+----[ end of %s ]", val_name.psz_string );
1140         }
1141         free( val_name.psz_string );
1142     }
1143 out:
1144     vlc_object_release( p_input );
1145     return i_error;
1146 }
1147
1148 static void print_playlist( intf_thread_t *p_intf, playlist_item_t *p_item, int i_level )
1149 {
1150     int i;
1151     char psz_buffer[MSTRTIME_MAX_SIZE];
1152     for( i = 0; i< p_item->i_children; i++ )
1153     {
1154         if( p_item->pp_children[i]->p_input->i_duration != -1 )
1155         {
1156             secstotimestr( psz_buffer, p_item->pp_children[i]->p_input->i_duration / 1000000 );
1157             msg_rc( "|%*s- %s (%s)", 2 * i_level, "", p_item->pp_children[i]->p_input->psz_name, psz_buffer );
1158         }
1159         else
1160             msg_rc( "|%*s- %s", 2 * i_level, "", p_item->pp_children[i]->p_input->psz_name );
1161
1162         if( p_item->pp_children[i]->i_children >= 0 )
1163             print_playlist( p_intf, p_item->pp_children[i], i_level + 1 );
1164     }
1165 }
1166
1167 static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
1168                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1169 {
1170     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1171     vlc_value_t val;
1172
1173     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1174     playlist_t *p_playlist = p_intf->p_sys->p_playlist;
1175     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1176
1177     if( p_input )
1178     {
1179         int state = var_GetInteger( p_input, "state" );
1180         vlc_object_release( p_input );
1181
1182         if( state == PAUSE_S )
1183         {
1184             msg_rc( "%s", _("Type 'pause' to continue.") );
1185             return VLC_EGENERIC;
1186         }
1187     }
1188
1189     /* Parse commands that require a playlist */
1190     if( !strcmp( psz_cmd, "prev" ) )
1191     {
1192         playlist_Prev( p_playlist );
1193     }
1194     else if( !strcmp( psz_cmd, "next" ) )
1195     {
1196         playlist_Next( p_playlist );
1197     }
1198     else if( !strcmp( psz_cmd, "play" ) )
1199     {
1200         msg_Warn( p_playlist, "play" );
1201         playlist_Play( p_playlist );
1202     }
1203     else if( !strcmp( psz_cmd, "repeat" ) )
1204     {
1205         bool b_update = true;
1206
1207         var_Get( p_playlist, "repeat", &val );
1208
1209         if( strlen( newval.psz_string ) > 0 )
1210         {
1211             if ( ( !strncmp( newval.psz_string, "on", 2 )  &&  val.b_bool ) ||
1212                  ( !strncmp( newval.psz_string, "off", 3 ) && !val.b_bool ) )
1213             {
1214                 b_update = false;
1215             }
1216         }
1217
1218         if ( b_update )
1219         {
1220             val.b_bool = !val.b_bool;
1221             var_Set( p_playlist, "repeat", val );
1222         }
1223         msg_rc( "Setting repeat to %d", val.b_bool );
1224     }
1225     else if( !strcmp( psz_cmd, "loop" ) )
1226     {
1227         bool b_update = true;
1228
1229         var_Get( p_playlist, "loop", &val );
1230
1231         if( strlen( newval.psz_string ) > 0 )
1232         {
1233             if ( ( !strncmp( newval.psz_string, "on", 2 )  &&  val.b_bool ) ||
1234                  ( !strncmp( newval.psz_string, "off", 3 ) && !val.b_bool ) )
1235             {
1236                 b_update = false;
1237             }
1238         }
1239
1240         if ( b_update )
1241         {
1242             val.b_bool = !val.b_bool;
1243             var_Set( p_playlist, "loop", val );
1244         }
1245         msg_rc( "Setting loop to %d", val.b_bool );
1246     }
1247     else if( !strcmp( psz_cmd, "random" ) )
1248     {
1249         bool b_update = true;
1250
1251         var_Get( p_playlist, "random", &val );
1252
1253         if( strlen( newval.psz_string ) > 0 )
1254         {
1255             if ( ( !strncmp( newval.psz_string, "on", 2 )  &&  val.b_bool ) ||
1256                  ( !strncmp( newval.psz_string, "off", 3 ) && !val.b_bool ) )
1257             {
1258                 b_update = false;
1259             }
1260         }
1261
1262         if ( b_update )
1263         {
1264             val.b_bool = !val.b_bool;
1265             var_Set( p_playlist, "random", val );
1266         }
1267         msg_rc( "Setting random to %d", val.b_bool );
1268     }
1269     else if (!strcmp( psz_cmd, "goto" ) )
1270     {
1271         PL_LOCK;
1272         unsigned i_pos = atoi( newval.psz_string );
1273         unsigned i_size = p_playlist->items.i_size;
1274
1275         if( i_pos <= 0 )
1276             msg_rc( "%s", _("Error: `goto' needs an argument greater than zero.") );
1277         else if( i_pos <= i_size )
1278         {
1279             playlist_item_t *p_item, *p_parent;
1280             p_item = p_parent = p_playlist->items.p_elems[i_pos-1];
1281             while( p_parent->p_parent )
1282                 p_parent = p_parent->p_parent;
1283             playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Locked,
1284                     p_parent, p_item );
1285         }
1286         else
1287             msg_rc( vlc_ngettext("Playlist has only %u element",
1288                                  "Playlist has only %u elements", i_size),
1289                      i_size );
1290         PL_UNLOCK;
1291     }
1292     else if( !strcmp( psz_cmd, "stop" ) )
1293     {
1294         playlist_Stop( p_playlist );
1295     }
1296     else if( !strcmp( psz_cmd, "clear" ) )
1297     {
1298         playlist_Stop( p_playlist );
1299         playlist_Clear( p_playlist, pl_Unlocked );
1300     }
1301     else if( !strcmp( psz_cmd, "add" ) &&
1302              newval.psz_string && *newval.psz_string )
1303     {
1304         input_item_t *p_item = parse_MRL( newval.psz_string );
1305
1306         if( p_item )
1307         {
1308             msg_rc( "Trying to add %s to playlist.", newval.psz_string );
1309             int i_ret =playlist_AddInput( p_playlist, p_item,
1310                      PLAYLIST_GO|PLAYLIST_APPEND, PLAYLIST_END, true,
1311                      pl_Unlocked );
1312             vlc_gc_decref( p_item );
1313             if( i_ret != VLC_SUCCESS )
1314             {
1315                 return VLC_EGENERIC;
1316             }
1317         }
1318     }
1319     else if( !strcmp( psz_cmd, "enqueue" ) &&
1320              newval.psz_string && *newval.psz_string )
1321     {
1322         input_item_t *p_item = parse_MRL( newval.psz_string );
1323
1324         if( p_item )
1325         {
1326             msg_rc( "trying to enqueue %s to playlist", newval.psz_string );
1327             if( playlist_AddInput( p_playlist, p_item,
1328                                PLAYLIST_APPEND, PLAYLIST_END, true,
1329                                pl_Unlocked ) != VLC_SUCCESS )
1330             {
1331                 return VLC_EGENERIC;
1332             }
1333         }
1334     }
1335     else if( !strcmp( psz_cmd, "playlist" ) )
1336     {
1337         msg_rc( "+----[ Playlist ]" );
1338         print_playlist( p_intf, p_playlist->p_root_category, 0 );
1339         msg_rc( "+----[ End of playlist ]" );
1340     }
1341
1342     else if( !strcmp( psz_cmd, "sort" ))
1343     {
1344         PL_LOCK;
1345         playlist_RecursiveNodeSort( p_playlist, p_playlist->p_root_onelevel,
1346                                     SORT_ARTIST, ORDER_NORMAL );
1347         PL_UNLOCK;
1348     }
1349     else if( !strcmp( psz_cmd, "status" ) )
1350     {
1351         input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1352         if( p_input )
1353         {
1354             /* Replay the current state of the system. */
1355             char *psz_uri =
1356                     input_item_GetURI( input_GetItem( p_input ) );
1357             vlc_object_release( p_input );
1358             if( likely(psz_uri != NULL) )
1359             {
1360                 msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
1361                 free( psz_uri );
1362             }
1363         }
1364
1365         float volume = playlist_VolumeGet( p_playlist );
1366         if( volume >= 0.f )
1367             msg_rc( STATUS_CHANGE "( audio volume: %ld )",
1368                     lroundf(volume * AOUT_VOLUME_DEFAULT) );
1369
1370         int status;
1371         PL_LOCK;
1372         status = playlist_Status(p_playlist);
1373         PL_UNLOCK;
1374         switch( status )
1375         {
1376             case PLAYLIST_STOPPED:
1377                 msg_rc( STATUS_CHANGE "( stop state: 5 )" );
1378                 break;
1379             case PLAYLIST_RUNNING:
1380                 msg_rc( STATUS_CHANGE "( play state: 3 )" );
1381                 break;
1382             case PLAYLIST_PAUSED:
1383                 msg_rc( STATUS_CHANGE "( pause state: 4 )" );
1384                 break;
1385             default:
1386                 msg_rc( STATUS_CHANGE "( unknown state: -1 )" );
1387                 break;
1388         }
1389     }
1390
1391     /*
1392      * sanity check
1393      */
1394     else
1395     {
1396         msg_rc( "unknown command!" );
1397     }
1398
1399     return VLC_SUCCESS;
1400 }
1401
1402 static int Quit( vlc_object_t *p_this, char const *psz_cmd,
1403                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1404 {
1405     VLC_UNUSED(p_data); VLC_UNUSED(psz_cmd);
1406     VLC_UNUSED(oldval); VLC_UNUSED(newval);
1407
1408     libvlc_Quit( p_this->p_libvlc );
1409     return VLC_SUCCESS;
1410 }
1411
1412 static int Intf( vlc_object_t *p_this, char const *psz_cmd,
1413                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1414 {
1415     intf_thread_t *intf = (intf_thread_t *)p_this;
1416
1417     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1418     return intf_Create(pl_Get(intf), newval.psz_string );
1419 }
1420
1421 static int Volume( vlc_object_t *p_this, char const *psz_cmd,
1422                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
1423 {
1424     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1425     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1426     playlist_t *p_playlist = p_intf->p_sys->p_playlist;
1427     input_thread_t *p_input = playlist_CurrentInput( p_playlist );
1428     int i_error = VLC_EGENERIC;
1429
1430     if( !p_input )
1431         return VLC_ENOOBJ;
1432
1433     if( p_input )
1434     {
1435         int state = var_GetInteger( p_input, "state" );
1436         vlc_object_release( p_input );
1437         if( state == PAUSE_S )
1438         {
1439             msg_rc( "%s", _("Type 'pause' to continue.") );
1440             return VLC_EGENERIC;
1441         }
1442     }
1443
1444     if ( *newval.psz_string )
1445     {
1446         /* Set. */
1447         int i_volume = atoi( newval.psz_string );
1448         if( !playlist_VolumeSet( p_playlist,
1449                              i_volume / (float)AOUT_VOLUME_DEFAULT ) )
1450             i_error = VLC_SUCCESS;
1451         playlist_MuteSet( p_playlist, i_volume == 0 );
1452         msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1453     }
1454     else
1455     {
1456         /* Get. */
1457         msg_rc( STATUS_CHANGE "( audio volume: %ld )",
1458                lroundf( playlist_VolumeGet( p_playlist ) * AOUT_VOLUME_DEFAULT ) );
1459         i_error = VLC_SUCCESS;
1460     }
1461
1462     return i_error;
1463 }
1464
1465 static int VolumeMove( vlc_object_t *p_this, char const *psz_cmd,
1466                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1467 {
1468     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1469     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1470     float volume;
1471     input_thread_t *p_input =
1472         playlist_CurrentInput( p_intf->p_sys->p_playlist );
1473     int i_nb_steps = atoi(newval.psz_string);
1474     int i_error = VLC_SUCCESS;
1475
1476     if( !p_input )
1477         return VLC_ENOOBJ;
1478
1479     int state = var_GetInteger( p_input, "state" );
1480     vlc_object_release( p_input );
1481     if( state == PAUSE_S )
1482     {
1483         msg_rc( "%s", _("Type 'pause' to continue.") );
1484         return VLC_EGENERIC;
1485     }
1486
1487     if( !strcmp(psz_cmd, "voldown") )
1488         i_nb_steps *= -1;
1489     if( playlist_VolumeUp( p_intf->p_sys->p_playlist, i_nb_steps, &volume ) < 0 )
1490         i_error = VLC_EGENERIC;
1491
1492     if ( !i_error )
1493         msg_rc( STATUS_CHANGE "( audio volume: %ld )",
1494                 lroundf( volume * AOUT_VOLUME_DEFAULT ) );
1495     return i_error;
1496 }
1497
1498
1499 static int VideoConfig( vlc_object_t *p_this, char const *psz_cmd,
1500                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1501 {
1502     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1503     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1504     input_thread_t *p_input =
1505         playlist_CurrentInput( p_intf->p_sys->p_playlist );
1506     vout_thread_t * p_vout;
1507     const char * psz_variable = NULL;
1508     int i_error = VLC_SUCCESS;
1509
1510     if( !p_input )
1511         return VLC_ENOOBJ;
1512
1513     p_vout = input_GetVout( p_input );
1514     vlc_object_release( p_input );
1515     if( !p_vout )
1516         return VLC_ENOOBJ;
1517
1518     if( !strcmp( psz_cmd, "vcrop" ) )
1519     {
1520         psz_variable = "crop";
1521     }
1522     else if( !strcmp( psz_cmd, "vratio" ) )
1523     {
1524         psz_variable = "aspect-ratio";
1525     }
1526     else if( !strcmp( psz_cmd, "vzoom" ) )
1527     {
1528         psz_variable = "zoom";
1529     }
1530     else if( !strcmp( psz_cmd, "snapshot" ) )
1531     {
1532         psz_variable = "video-snapshot";
1533     }
1534     else
1535         /* This case can't happen */
1536         vlc_assert_unreachable();
1537
1538     if( newval.psz_string && *newval.psz_string )
1539     {
1540         /* set */
1541         if( !strcmp( psz_variable, "zoom" ) )
1542         {
1543             vlc_value_t val;
1544             val.f_float = atof( newval.psz_string );
1545             i_error = var_Set( p_vout, psz_variable, val );
1546         }
1547         else
1548         {
1549             i_error = var_Set( p_vout, psz_variable, newval );
1550         }
1551     }
1552     else if( !strcmp( psz_cmd, "snapshot" ) )
1553     {
1554         var_TriggerCallback( p_vout, psz_variable );
1555     }
1556     else
1557     {
1558         /* get */
1559         vlc_value_t val_name;
1560         vlc_value_t val, text;
1561         int i;
1562         float f_value = 0.;
1563         char *psz_value = NULL;
1564
1565         if ( var_Get( p_vout, psz_variable, &val ) < 0 )
1566         {
1567             vlc_object_release( p_vout );
1568             return VLC_EGENERIC;
1569         }
1570         if( !strcmp( psz_variable, "zoom" ) )
1571         {
1572             f_value = val.f_float;
1573         }
1574         else
1575         {
1576             psz_value = val.psz_string;
1577         }
1578
1579         if ( var_Change( p_vout, psz_variable,
1580                          VLC_VAR_GETCHOICES, &val, &text ) < 0 )
1581         {
1582             vlc_object_release( p_vout );
1583             free( psz_value );
1584             return VLC_EGENERIC;
1585         }
1586
1587         /* Get the descriptive name of the variable */
1588         var_Change( p_vout, psz_variable, VLC_VAR_GETTEXT,
1589                     &val_name, NULL );
1590         if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1591
1592         msg_rc( "+----[ %s ]", val_name.psz_string );
1593         if( !strcmp( psz_variable, "zoom" ) )
1594         {
1595             for ( i = 0; i < val.p_list->i_count; i++ )
1596             {
1597                 if ( f_value == val.p_list->p_values[i].f_float )
1598                     msg_rc( "| %f - %s *", val.p_list->p_values[i].f_float,
1599                             text.p_list->p_values[i].psz_string );
1600                 else
1601                     msg_rc( "| %f - %s", val.p_list->p_values[i].f_float,
1602                             text.p_list->p_values[i].psz_string );
1603             }
1604         }
1605         else
1606         {
1607             for ( i = 0; i < val.p_list->i_count; i++ )
1608             {
1609                 if ( !strcmp( psz_value, val.p_list->p_values[i].psz_string ) )
1610                     msg_rc( "| %s - %s *", val.p_list->p_values[i].psz_string,
1611                             text.p_list->p_values[i].psz_string );
1612                 else
1613                     msg_rc( "| %s - %s", val.p_list->p_values[i].psz_string,
1614                             text.p_list->p_values[i].psz_string );
1615             }
1616             free( psz_value );
1617         }
1618         var_FreeList( &val, &text );
1619         msg_rc( "+----[ end of %s ]", val_name.psz_string );
1620
1621         free( val_name.psz_string );
1622     }
1623     vlc_object_release( p_vout );
1624     return i_error;
1625 }
1626
1627 static int AudioDevice( vlc_object_t *obj, char const *cmd,
1628                         vlc_value_t old, vlc_value_t cur, void *dummy )
1629 {
1630     intf_thread_t *p_intf = (intf_thread_t *)obj;
1631     audio_output_t *p_aout = playlist_GetAout( pl_Get(p_intf) );
1632     if( p_aout == NULL )
1633         return VLC_ENOOBJ;
1634
1635     if( !*cur.psz_string )
1636     {
1637         char **ids, **names;
1638         int n = aout_DevicesList( p_aout, &ids, &names );
1639         if( n < 0 )
1640             goto out;
1641
1642         char *dev = aout_DeviceGet( p_aout );
1643         const char *devstr = (dev != NULL) ? dev : "";
1644
1645         msg_rc( "+----[ %s ]", cmd );
1646         for ( int i = 0; i < n; i++ )
1647         {
1648             const char *fmt = "| %s - %s";
1649
1650             if( !strcmp(devstr, ids[i]) )
1651                 fmt = "| %s - %s *";
1652             msg_rc( fmt, ids[i], names[i] );
1653             free( names[i] );
1654             free( ids[i] );
1655         }
1656         msg_rc( "+----[ end of %s ]", cmd );
1657
1658         free( dev );
1659         free( names );
1660         free( ids );
1661     }
1662     else
1663         aout_DeviceSet( p_aout, cur.psz_string );
1664 out:
1665     vlc_object_release( p_aout );
1666     (void) old; (void) dummy;
1667     return VLC_SUCCESS;
1668 }
1669
1670 static int AudioChannel( vlc_object_t *obj, char const *cmd,
1671                          vlc_value_t old, vlc_value_t cur, void *dummy )
1672 {
1673     intf_thread_t *p_intf = (intf_thread_t*)obj;
1674     vlc_object_t *p_aout = (vlc_object_t *)playlist_GetAout( pl_Get(p_intf) );
1675     if ( p_aout == NULL )
1676          return VLC_ENOOBJ;
1677
1678     int ret = VLC_SUCCESS;
1679
1680     if ( !*cur.psz_string )
1681     {
1682         /* Retrieve all registered ***. */
1683         vlc_value_t val, text;
1684         if ( var_Change( p_aout, "stereo-mode",
1685                          VLC_VAR_GETCHOICES, &val, &text ) < 0 )
1686         {
1687             ret = VLC_ENOVAR;
1688             goto out;
1689         }
1690
1691         int i_value = var_GetInteger( p_aout, "stereo-mode" );
1692
1693         msg_rc( "+----[ %s ]", cmd );
1694         for ( int i = 0; i < val.p_list->i_count; i++ )
1695         {
1696             if ( i_value == val.p_list->p_values[i].i_int )
1697                 msg_rc( "| %"PRId64" - %s *", val.p_list->p_values[i].i_int,
1698                         text.p_list->p_values[i].psz_string );
1699             else
1700                 msg_rc( "| %"PRId64" - %s", val.p_list->p_values[i].i_int,
1701                         text.p_list->p_values[i].psz_string );
1702         }
1703         var_FreeList( &val, &text );
1704         msg_rc( "+----[ end of %s ]", cmd );
1705     }
1706     else
1707         ret = var_SetInteger( p_aout, "stereo-mode", atoi( cur.psz_string ) );
1708 out:
1709     vlc_object_release( p_aout );
1710     (void) old; (void) dummy;
1711     return ret;
1712 }
1713
1714 static int Statistics ( vlc_object_t *p_this, char const *psz_cmd,
1715     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1716 {
1717     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
1718     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1719     input_thread_t *p_input =
1720         playlist_CurrentInput( p_intf->p_sys->p_playlist );
1721
1722     if( !p_input )
1723         return VLC_ENOOBJ;
1724
1725     updateStatistics( p_intf, input_GetItem(p_input) );
1726     vlc_object_release( p_input );
1727     return VLC_SUCCESS;
1728 }
1729
1730 static int updateStatistics( intf_thread_t *p_intf, input_item_t *p_item )
1731 {
1732     if( !p_item ) return VLC_EGENERIC;
1733
1734     vlc_mutex_lock( &p_item->lock );
1735     vlc_mutex_lock( &p_item->p_stats->lock );
1736     msg_rc( "+----[ begin of statistical info ]" );
1737
1738     /* Input */
1739     msg_rc("%s", _("+-[Incoming]"));
1740     msg_rc(_("| input bytes read : %8.0f KiB"),
1741             (float)(p_item->p_stats->i_read_bytes)/1024 );
1742     msg_rc(_("| input bitrate    :   %6.0f kb/s"),
1743             (float)(p_item->p_stats->f_input_bitrate)*8000 );
1744     msg_rc(_("| demux bytes read : %8.0f KiB"),
1745             (float)(p_item->p_stats->i_demux_read_bytes)/1024 );
1746     msg_rc(_("| demux bitrate    :   %6.0f kb/s"),
1747             (float)(p_item->p_stats->f_demux_bitrate)*8000 );
1748     msg_rc(_("| demux corrupted  :    %5"PRIi64),
1749             p_item->p_stats->i_demux_corrupted );
1750     msg_rc(_("| discontinuities  :    %5"PRIi64),
1751             p_item->p_stats->i_demux_discontinuity );
1752     msg_rc("|");
1753     /* Video */
1754     msg_rc("%s", _("+-[Video Decoding]"));
1755     msg_rc(_("| video decoded    :    %5"PRIi64),
1756             p_item->p_stats->i_decoded_video );
1757     msg_rc(_("| frames displayed :    %5"PRIi64),
1758             p_item->p_stats->i_displayed_pictures );
1759     msg_rc(_("| frames lost      :    %5"PRIi64),
1760             p_item->p_stats->i_lost_pictures );
1761     msg_rc("|");
1762     /* Audio*/
1763     msg_rc("%s", _("+-[Audio Decoding]"));
1764     msg_rc(_("| audio decoded    :    %5"PRIi64),
1765             p_item->p_stats->i_decoded_audio );
1766     msg_rc(_("| buffers played   :    %5"PRIi64),
1767             p_item->p_stats->i_played_abuffers );
1768     msg_rc(_("| buffers lost     :    %5"PRIi64),
1769             p_item->p_stats->i_lost_abuffers );
1770     msg_rc("|");
1771     /* Sout */
1772     msg_rc("%s", _("+-[Streaming]"));
1773     msg_rc(_("| packets sent     :    %5"PRIi64),
1774            p_item->p_stats->i_sent_packets );
1775     msg_rc(_("| bytes sent       : %8.0f KiB"),
1776             (float)(p_item->p_stats->i_sent_bytes)/1024 );
1777     msg_rc(_("| sending bitrate  :   %6.0f kb/s"),
1778             (float)(p_item->p_stats->f_send_bitrate*8)*1000 );
1779     msg_rc("|");
1780     msg_rc( "+----[ end of statistical info ]" );
1781     vlc_mutex_unlock( &p_item->p_stats->lock );
1782     vlc_mutex_unlock( &p_item->lock );
1783
1784     return VLC_SUCCESS;
1785 }
1786
1787 #ifdef _WIN32
1788 static bool ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
1789 {
1790     INPUT_RECORD input_record;
1791     DWORD i_dw;
1792
1793     /* On Win32, select() only works on socket descriptors */
1794     while( WaitForSingleObjectEx( p_intf->p_sys->hConsoleIn,
1795                                 INTF_IDLE_SLEEP/1000, TRUE ) == WAIT_OBJECT_0 )
1796     {
1797         while( *pi_size < MAX_LINE_LENGTH &&
1798                ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record,
1799                                  1, &i_dw ) )
1800         {
1801             if( input_record.EventType != KEY_EVENT ||
1802                 !input_record.Event.KeyEvent.bKeyDown ||
1803                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
1804                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL||
1805                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU ||
1806                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL )
1807             {
1808                 /* nothing interesting */
1809                 continue;
1810             }
1811
1812             p_buffer[ *pi_size ] = input_record.Event.KeyEvent.uChar.AsciiChar;
1813
1814             /* Echo out the command */
1815             putc( p_buffer[ *pi_size ], stdout );
1816
1817             /* Handle special keys */
1818             if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1819             {
1820                 putc( '\n', stdout );
1821                 break;
1822             }
1823             switch( p_buffer[ *pi_size ] )
1824             {
1825             case '\b':
1826                 if( *pi_size )
1827                 {
1828                     *pi_size -= 2;
1829                     putc( ' ', stdout );
1830                     putc( '\b', stdout );
1831                 }
1832                 break;
1833             case '\r':
1834                 (*pi_size) --;
1835                 break;
1836             }
1837
1838             (*pi_size)++;
1839         }
1840
1841         if( *pi_size == MAX_LINE_LENGTH ||
1842             p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1843         {
1844             p_buffer[ *pi_size ] = 0;
1845             return true;
1846         }
1847     }
1848
1849     vlc_testcancel ();
1850
1851     return false;
1852 }
1853 #endif
1854
1855 bool ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
1856 {
1857 #ifdef _WIN32
1858     if( p_intf->p_sys->i_socket == -1 && !p_intf->p_sys->b_quiet )
1859         return ReadWin32( p_intf, p_buffer, pi_size );
1860     else if( p_intf->p_sys->i_socket == -1 )
1861     {
1862         msleep( INTF_IDLE_SLEEP );
1863         return false;
1864     }
1865 #endif
1866
1867     while( *pi_size < MAX_LINE_LENGTH )
1868     {
1869         if( p_intf->p_sys->i_socket == -1 )
1870         {
1871             if( read( 0/*STDIN_FILENO*/, p_buffer + *pi_size, 1 ) <= 0 )
1872             {   /* Standard input closed: exit */
1873                 vlc_value_t empty;
1874                 Quit( VLC_OBJECT(p_intf), NULL, empty, empty, NULL );
1875                 p_buffer[*pi_size] = 0;
1876                 return true;
1877             }
1878         }
1879         else
1880         {   /* Connection closed */
1881             if( net_Read( p_intf, p_intf->p_sys->i_socket, p_buffer + *pi_size,
1882                           1 ) <= 0 )
1883             {
1884                 net_Close( p_intf->p_sys->i_socket );
1885                 p_intf->p_sys->i_socket = -1;
1886                 p_buffer[*pi_size] = 0;
1887                 return true;
1888             }
1889         }
1890
1891         if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1892             break;
1893
1894         (*pi_size)++;
1895     }
1896
1897     if( *pi_size == MAX_LINE_LENGTH ||
1898         p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1899     {
1900         p_buffer[ *pi_size ] = 0;
1901         return true;
1902     }
1903
1904     return false;
1905 }
1906
1907 /*****************************************************************************
1908  * parse_MRL: build a input item from a full mrl
1909  *****************************************************************************
1910  * MRL format: "simplified-mrl [:option-name[=option-value]]"
1911  * We don't check for '"' or '\'', we just assume that a ':' that follows a
1912  * space is a new option. Should be good enough for our purpose.
1913  *****************************************************************************/
1914 static input_item_t *parse_MRL( const char *mrl )
1915 {
1916 #define SKIPSPACE( p ) { while( *p == ' ' || *p == '\t' ) p++; }
1917 #define SKIPTRAILINGSPACE( p, d ) \
1918     { char *e=d; while( e > p && (*(e-1)==' ' || *(e-1)=='\t') ){e--;*e=0;} }
1919
1920     input_item_t *p_item = NULL;
1921     char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig, *psz_mrl;
1922     char **ppsz_options = NULL;
1923     int i, i_options = 0;
1924
1925     if( !mrl ) return 0;
1926
1927     psz_mrl = psz_orig = strdup( mrl );
1928     if( !psz_mrl )
1929         return NULL;
1930     while( *psz_mrl )
1931     {
1932         SKIPSPACE( psz_mrl );
1933         psz_item = psz_mrl;
1934
1935         for( ; *psz_mrl; psz_mrl++ )
1936         {
1937             if( (*psz_mrl == ' ' || *psz_mrl == '\t') && psz_mrl[1] == ':' )
1938             {
1939                 /* We have a complete item */
1940                 break;
1941             }
1942             if( (*psz_mrl == ' ' || *psz_mrl == '\t') &&
1943                 (psz_mrl[1] == '"' || psz_mrl[1] == '\'') && psz_mrl[2] == ':')
1944             {
1945                 /* We have a complete item */
1946                 break;
1947             }
1948         }
1949
1950         if( *psz_mrl ) { *psz_mrl = 0; psz_mrl++; }
1951         SKIPTRAILINGSPACE( psz_item, psz_item + strlen( psz_item ) );
1952
1953         /* Remove '"' and '\'' if necessary */
1954         if( *psz_item == '"' && psz_item[strlen(psz_item)-1] == '"' )
1955         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
1956         if( *psz_item == '\'' && psz_item[strlen(psz_item)-1] == '\'' )
1957         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
1958
1959         if( !psz_item_mrl )
1960         {
1961             if( strstr( psz_item, "://" ) != NULL )
1962                 psz_item_mrl = strdup( psz_item );
1963             else
1964                 psz_item_mrl = vlc_path2uri( psz_item, NULL );
1965             if( psz_item_mrl == NULL )
1966             {
1967                 free( psz_orig );
1968                 return NULL;
1969             }
1970         }
1971         else if( *psz_item )
1972         {
1973             i_options++;
1974             ppsz_options = xrealloc( ppsz_options, i_options * sizeof(char *) );
1975             ppsz_options[i_options - 1] = &psz_item[1];
1976         }
1977
1978         if( *psz_mrl ) SKIPSPACE( psz_mrl );
1979     }
1980
1981     /* Now create a playlist item */
1982     if( psz_item_mrl )
1983     {
1984         p_item = input_item_New( psz_item_mrl, NULL );
1985         for( i = 0; i < i_options; i++ )
1986         {
1987             input_item_AddOption( p_item, ppsz_options[i], VLC_INPUT_OPTION_TRUSTED );
1988         }
1989         free( psz_item_mrl );
1990     }
1991
1992     if( i_options ) free( ppsz_options );
1993     free( psz_orig );
1994
1995     return p_item;
1996 }