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