]> git.sesse.net Git - vlc/blob - src/misc/objects.c
var_LocationParse: helper for DVB/V4L2-style MRLs
[vlc] / src / misc / objects.c
1 /*****************************************************************************
2  * objects.c: vlc_object_t handling
3  *****************************************************************************
4  * Copyright (C) 2004-2008 the VideoLAN team
5  * Copyright (C) 2006-2010 RĂ©mi Denis-Courmont
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /**
25  * \file
26  * This file contains the functions to handle the vlc_object_t type
27  *
28  * Unless otherwise stated, functions in this file are not cancellation point.
29  * All functions in this file are safe w.r.t. deferred cancellation.
30  */
31
32
33 /*****************************************************************************
34  * Preamble
35  *****************************************************************************/
36 #ifdef HAVE_CONFIG_H
37 # include "config.h"
38 #endif
39
40 #include <vlc_common.h>
41
42 #include "../libvlc.h"
43 #include <vlc_aout.h>
44 #include "audio_output/aout_internal.h"
45
46 #include "vlc_interface.h"
47 #include "vlc_codec.h"
48
49 #include "variables.h"
50
51 #ifdef HAVE_SEARCH_H
52 # include <search.h>
53 #endif
54
55 #ifndef WIN32
56 # include <vlc_fs.h>
57 # include <unistd.h>
58 #else
59 # include <io.h>
60 # include <winsock2.h>
61 # include <ws2tcpip.h>
62 # undef  read
63 # define read( a, b, c )  recv (a, b, c, 0)
64 # undef  write
65 # define write( a, b, c ) send (a, b, c, 0)
66 # undef  close
67 # define close( a )       closesocket (a)
68 #endif
69
70 #include <limits.h>
71 #include <assert.h>
72
73 #if defined (HAVE_SYS_EVENTFD_H)
74 # include <sys/eventfd.h>
75 # ifndef EFD_CLOEXEC
76 #  define EFD_CLOEXEC 0
77 #  warning EFD_CLOEXEC missing. Consider updating libc.
78 # endif
79 #endif
80
81
82 /*****************************************************************************
83  * Local prototypes
84  *****************************************************************************/
85 static int  DumpCommand( vlc_object_t *, char const *,
86                          vlc_value_t, vlc_value_t, void * );
87
88 static vlc_object_t * FindParent    ( vlc_object_t *, int );
89 static vlc_object_t * FindChild     ( vlc_object_internals_t *, int );
90 static vlc_object_t * FindParentName( vlc_object_t *, const char * );
91 static vlc_object_t * FindChildName ( vlc_object_internals_t *, const char * );
92 static void PrintObject( vlc_object_internals_t *, const char * );
93 static void DumpStructure( vlc_object_internals_t *, unsigned, char * );
94
95 static vlc_list_t   * NewList       ( int );
96
97 static void vlc_object_destroy( vlc_object_t *p_this );
98
99 /*****************************************************************************
100  * Local structure lock
101  *****************************************************************************/
102 static void libvlc_lock (libvlc_int_t *p_libvlc)
103 {
104     vlc_mutex_lock (&(libvlc_priv (p_libvlc)->structure_lock));
105 }
106
107 static void libvlc_unlock (libvlc_int_t *p_libvlc)
108 {
109     vlc_mutex_unlock (&(libvlc_priv (p_libvlc)->structure_lock));
110 }
111
112 #undef vlc_custom_create
113 void *vlc_custom_create( vlc_object_t *p_this, size_t i_size,
114                          int i_type, const char *psz_type )
115 {
116     vlc_object_t *p_new;
117     vlc_object_internals_t *p_priv;
118
119     /* NOTE:
120      * VLC objects are laid out as follow:
121      * - first the LibVLC-private per-object data,
122      * - then VLC_COMMON members from vlc_object_t,
123      * - finally, the type-specific data (if any).
124      *
125      * This function initializes the LibVLC and common data,
126      * and zeroes the rest.
127      */
128     p_priv = calloc( 1, sizeof( *p_priv ) + i_size );
129     if( p_priv == NULL )
130         return NULL;
131
132     assert (i_size >= sizeof (vlc_object_t));
133     p_new = (vlc_object_t *)(p_priv + 1);
134
135     p_priv->i_object_type = i_type;
136     p_new->psz_object_type = psz_type;
137     p_priv->psz_name = NULL;
138
139     p_new->b_die = false;
140     p_new->b_force = false;
141
142     p_new->psz_header = NULL;
143
144     if (p_this)
145         p_new->i_flags = p_this->i_flags
146             & (OBJECT_FLAGS_NODBG|OBJECT_FLAGS_QUIET|OBJECT_FLAGS_NOINTERACT);
147
148     p_priv->var_root = NULL;
149
150     if( p_this == NULL )
151     {
152         libvlc_int_t *self = (libvlc_int_t*)p_new;
153         p_new->p_libvlc = self;
154         vlc_mutex_init (&(libvlc_priv (self)->structure_lock));
155         p_this = p_new;
156     }
157     else
158         p_new->p_libvlc = p_this->p_libvlc;
159
160     vlc_spin_init( &p_priv->ref_spin );
161     p_priv->i_refcount = 1;
162     p_priv->pf_destructor = NULL;
163     p_priv->b_thread = false;
164     p_new->p_parent = NULL;
165     p_priv->first = NULL;
166
167     /* Initialize mutexes and condvars */
168     vlc_mutex_init( &p_priv->var_lock );
169     vlc_cond_init( &p_priv->var_wait );
170     p_priv->pipes[0] = p_priv->pipes[1] = -1;
171
172     if (p_new == VLC_OBJECT(p_new->p_libvlc))
173     {   /* TODO: should be in src/libvlc.c */
174         int canc = vlc_savecancel ();
175         var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
176         var_AddCallback( p_new, "tree", DumpCommand, NULL );
177         var_Create( p_new, "vars", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
178         var_AddCallback( p_new, "vars", DumpCommand, NULL );
179         vlc_restorecancel (canc);
180     }
181
182     return p_new;
183 }
184
185 #undef vlc_object_create
186 /**
187  * Allocates and initializes a vlc object.
188  *
189  * @param i_size object byte size
190  *
191  * @return the new object, or NULL on error.
192  */
193 void *vlc_object_create( vlc_object_t *p_this, size_t i_size )
194 {
195     return vlc_custom_create( p_this, i_size, VLC_OBJECT_GENERIC, "generic" );
196 }
197
198 #undef vlc_object_set_destructor
199 /**
200  ****************************************************************************
201  * Set the destructor of a vlc object
202  *
203  * This function sets the destructor of the vlc object. It will be called
204  * when the object is destroyed when the its refcount reaches 0.
205  * (It is called by the internal function vlc_object_destroy())
206  *****************************************************************************/
207 void vlc_object_set_destructor( vlc_object_t *p_this,
208                                 vlc_destructor_t pf_destructor )
209 {
210     vlc_object_internals_t *p_priv = vlc_internals(p_this );
211
212     vlc_spin_lock( &p_priv->ref_spin );
213     p_priv->pf_destructor = pf_destructor;
214     vlc_spin_unlock( &p_priv->ref_spin );
215 }
216
217 static vlc_mutex_t name_lock = VLC_STATIC_MUTEX;
218
219 #undef vlc_object_set_name
220 int vlc_object_set_name(vlc_object_t *obj, const char *name)
221 {
222     vlc_object_internals_t *priv = vlc_internals(obj);
223     char *newname = name ? strdup (name) : NULL;
224     char *oldname;
225
226     vlc_mutex_lock (&name_lock);
227     oldname = priv->psz_name;
228     priv->psz_name = newname;
229     vlc_mutex_unlock (&name_lock);
230
231     free (oldname);
232     return (priv->psz_name || !name) ? VLC_SUCCESS : VLC_ENOMEM;
233 }
234
235 #undef vlc_object_get_name
236 char *vlc_object_get_name(const vlc_object_t *obj)
237 {
238     vlc_object_internals_t *priv = vlc_internals(obj);
239     char *name;
240
241     vlc_mutex_lock (&name_lock);
242     name = priv->psz_name ? strdup (priv->psz_name) : NULL;
243     vlc_mutex_unlock (&name_lock);
244
245     return name;
246 }
247
248 /**
249  ****************************************************************************
250  * Destroy a vlc object (Internal)
251  *
252  * This function destroys an object that has been previously allocated with
253  * vlc_object_create. The object's refcount must be zero and it must not be
254  * attached to other objects in any way.
255  *
256  * This function must be called with cancellation disabled (currently).
257  *****************************************************************************/
258 static void vlc_object_destroy( vlc_object_t *p_this )
259 {
260     vlc_object_internals_t *p_priv = vlc_internals( p_this );
261
262     /* Send a kill to the object's thread if applicable */
263     vlc_object_kill( p_this );
264
265     /* Call the custom "subclass" destructor */
266     if( p_priv->pf_destructor )
267         p_priv->pf_destructor( p_this );
268
269     /* Any thread must have been cleaned up at this point. */
270     assert( !p_priv->b_thread );
271
272     /* Destroy the associated variables. */
273     var_DestroyAll( p_this );
274
275     vlc_cond_destroy( &p_priv->var_wait );
276     vlc_mutex_destroy( &p_priv->var_lock );
277
278     free( p_this->psz_header );
279
280     free( p_priv->psz_name );
281
282     vlc_spin_destroy( &p_priv->ref_spin );
283     if( p_priv->pipes[1] != -1 && p_priv->pipes[1] != p_priv->pipes[0] )
284         close( p_priv->pipes[1] );
285     if( p_priv->pipes[0] != -1 )
286         close( p_priv->pipes[0] );
287     if( VLC_OBJECT(p_this->p_libvlc) == p_this )
288         vlc_mutex_destroy (&(libvlc_priv ((libvlc_int_t *)p_this)->structure_lock));
289
290     free( p_priv );
291 }
292
293
294 #ifdef WIN32
295 /**
296  * select()-able pipes emulated using Winsock
297  */
298 # define vlc_pipe selectable_pipe
299 static int selectable_pipe (int fd[2])
300 {
301     SOCKADDR_IN addr;
302     int addrlen = sizeof (addr);
303
304     SOCKET l = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP), a,
305            c = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
306     if ((l == INVALID_SOCKET) || (c == INVALID_SOCKET))
307         goto error;
308
309     memset (&addr, 0, sizeof (addr));
310     addr.sin_family = AF_INET;
311     addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
312     if (bind (l, (PSOCKADDR)&addr, sizeof (addr))
313      || getsockname (l, (PSOCKADDR)&addr, &addrlen)
314      || listen (l, 1)
315      || connect (c, (PSOCKADDR)&addr, addrlen))
316         goto error;
317
318     a = accept (l, NULL, NULL);
319     if (a == INVALID_SOCKET)
320         goto error;
321
322     closesocket (l);
323     //shutdown (a, 0);
324     //shutdown (c, 1);
325     fd[0] = c;
326     fd[1] = a;
327     return 0;
328
329 error:
330     if (l != INVALID_SOCKET)
331         closesocket (l);
332     if (c != INVALID_SOCKET)
333         closesocket (c);
334     return -1;
335 }
336 #endif /* WIN32 */
337
338 static vlc_mutex_t pipe_lock = VLC_STATIC_MUTEX;
339
340 /**
341  * Returns the readable end of a pipe that becomes readable once termination
342  * of the object is requested (vlc_object_kill()).
343  * This can be used to wake-up out of a select() or poll() event loop, such
344  * typically when doing network I/O.
345  *
346  * Note that the pipe will remain the same for the lifetime of the object.
347  * DO NOT read the pipe nor close it yourself. Ever.
348  *
349  * @param obj object that would be "killed"
350  * @return a readable pipe descriptor, or -1 on error.
351  */
352 int vlc_object_waitpipe( vlc_object_t *obj )
353 {
354     vlc_object_internals_t *internals = vlc_internals( obj );
355
356     vlc_mutex_lock (&pipe_lock);
357     if (internals->pipes[0] == -1)
358     {
359         /* This can only ever happen if someone killed us without locking: */
360         assert (internals->pipes[1] == -1);
361
362         /* pipe() is not a cancellation point, but write() is and eventfd() is
363          * unspecified (not in POSIX). */
364         int canc = vlc_savecancel ();
365 #if defined (HAVE_SYS_EVENTFD_H)
366         internals->pipes[0] = internals->pipes[1] = eventfd (0, EFD_CLOEXEC);
367         if (internals->pipes[0] == -1)
368 #endif
369         {
370             if (vlc_pipe (internals->pipes))
371                 internals->pipes[0] = internals->pipes[1] = -1;
372         }
373
374         if (internals->pipes[0] != -1 && obj->b_die)
375         {   /* Race condition: vlc_object_kill() already invoked! */
376             msg_Dbg (obj, "waitpipe: object already dying");
377             write (internals->pipes[1], &(uint64_t){ 1 }, sizeof (uint64_t));
378         }
379         vlc_restorecancel (canc);
380     }
381     vlc_mutex_unlock (&pipe_lock);
382     return internals->pipes[0];
383 }
384
385 #undef vlc_object_kill
386 /**
387  * Requests termination of an object, cancels the object thread, and make the
388  * object wait pipe (if it exists) readable. Not a cancellation point.
389  */
390 void vlc_object_kill( vlc_object_t *p_this )
391 {
392     vlc_object_internals_t *priv = vlc_internals( p_this );
393     int fd = -1;
394
395     vlc_thread_cancel( p_this );
396     vlc_mutex_lock( &pipe_lock );
397     if( !p_this->b_die )
398     {
399         fd = priv->pipes[1];
400         p_this->b_die = true;
401     }
402
403     /* This also serves as a memory barrier toward vlc_object_alive(): */
404     vlc_mutex_unlock( &pipe_lock );
405
406     if (fd != -1)
407     {
408         int canc = vlc_savecancel ();
409
410         /* write _after_ setting b_die, so vlc_object_alive() returns false */
411         write (fd, &(uint64_t){ 1 }, sizeof (uint64_t));
412         msg_Dbg (p_this, "waitpipe: object killed");
413         vlc_restorecancel (canc);
414     }
415 }
416
417 #undef vlc_object_find
418 /*****************************************************************************
419  * find a typed object and increment its refcount
420  *****************************************************************************
421  * This function recursively looks for a given object type. i_mode can be one
422  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
423  *****************************************************************************/
424 void * vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode )
425 {
426     vlc_object_t *p_found;
427
428     /* If we are of the requested type ourselves, don't look further */
429     if( vlc_internals (p_this)->i_object_type == i_type )
430     {
431         vlc_object_hold( p_this );
432         return p_this;
433     }
434
435     /* Otherwise, recursively look for the object */
436     if (i_mode == FIND_ANYWHERE)
437         return vlc_object_find (VLC_OBJECT(p_this->p_libvlc), i_type, FIND_CHILD);
438
439     switch (i_type)
440     {
441         case VLC_OBJECT_VOUT:
442         case VLC_OBJECT_AOUT:
443             break;
444         case VLC_OBJECT_INPUT:
445             /* input can only be accessed like this from children,
446              * otherwise we could not promise that it is initialized */
447             if (i_mode != FIND_PARENT)
448                 return NULL;
449             break;
450         default:
451             return NULL;
452     }
453
454     libvlc_lock (p_this->p_libvlc);
455     switch (i_mode)
456     {
457         case FIND_PARENT:
458             p_found = FindParent (p_this, i_type);
459             break;
460         case FIND_CHILD:
461             p_found = FindChild (vlc_internals (p_this), i_type);
462             break;
463         default:
464             assert (0);
465     }
466     libvlc_unlock (p_this->p_libvlc);
467     return p_found;
468 }
469
470
471 static int objnamecmp(const vlc_object_t *obj, const char *name)
472 {
473     char *objname = vlc_object_get_name(obj);
474     if (objname == NULL)
475         return INT_MIN;
476
477     int ret = strcmp (objname, name);
478     free (objname);
479     return ret;
480 }
481
482 #undef vlc_object_find_name
483 /**
484  * Finds a named object and increment its reference count.
485  * Beware that objects found in this manner can be "owned" by another thread,
486  * be of _any_ type, and be attached to any module (if any). With such an
487  * object reference, you can set or get object variables, emit log messages,
488  * and read write-once object parameters (psz_object_type, etc).
489  * You CANNOT cast the object to a more specific object type, and you
490  * definitely cannot invoke object type-specific callbacks with this.
491  *
492  * @param p_this object to search from
493  * @param psz_name name of the object to search for
494  * @param i_mode search direction: FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
495  *
496  * @return a matching object (must be released by the caller),
497  * or NULL on error.
498  */
499 vlc_object_t *vlc_object_find_name( vlc_object_t *p_this,
500                                     const char *psz_name, int i_mode )
501 {
502     vlc_object_t *p_found;
503
504     /* Reading psz_object_name from a separate inhibits thread-safety.
505      * Use a libvlc address variable instead for that sort of things! */
506     msg_Warn( p_this, "%s(%s) is not safe!", __func__, psz_name );
507     /* If have the requested name ourselves, don't look further */
508     if( !objnamecmp(p_this, psz_name) )
509     {
510         vlc_object_hold( p_this );
511         return p_this;
512     }
513
514     /* Otherwise, recursively look for the object */
515     if (i_mode == FIND_ANYWHERE)
516         return vlc_object_find_name (VLC_OBJECT(p_this->p_libvlc), psz_name,
517                                      FIND_CHILD);
518
519     libvlc_lock (p_this->p_libvlc);
520     vlc_mutex_lock (&name_lock);
521     switch (i_mode)
522     {
523         case FIND_PARENT:
524             p_found = FindParentName (p_this, psz_name);
525             break;
526         case FIND_CHILD:
527             p_found = FindChildName (vlc_internals (p_this), psz_name);
528             break;
529         default:
530             assert (0);
531     }
532     vlc_mutex_unlock (&name_lock);
533     libvlc_unlock (p_this->p_libvlc);
534     return p_found;
535 }
536
537 #undef vlc_object_hold
538 /**
539  * Increment an object reference counter.
540  */
541 void * vlc_object_hold( vlc_object_t *p_this )
542 {
543     vlc_object_internals_t *internals = vlc_internals( p_this );
544
545     vlc_spin_lock( &internals->ref_spin );
546     /* Avoid obvious freed object uses */
547     assert( internals->i_refcount > 0 );
548     /* Increment the counter */
549     internals->i_refcount++;
550     vlc_spin_unlock( &internals->ref_spin );
551     return p_this;
552 }
553
554 #undef vlc_object_release
555 /*****************************************************************************
556  * Decrement an object refcount
557  * And destroy the object if its refcount reach zero.
558  *****************************************************************************/
559 void vlc_object_release( vlc_object_t *p_this )
560 {
561     vlc_object_internals_t *internals = vlc_internals( p_this );
562     vlc_object_t *parent = NULL;
563     bool b_should_destroy;
564
565     vlc_spin_lock( &internals->ref_spin );
566     assert( internals->i_refcount > 0 );
567
568     if( internals->i_refcount > 1 )
569     {
570         /* Fast path */
571         /* There are still other references to the object */
572         internals->i_refcount--;
573         vlc_spin_unlock( &internals->ref_spin );
574         return;
575     }
576     vlc_spin_unlock( &internals->ref_spin );
577
578     /* Slow path */
579     /* Remember that we cannot hold the spin while waiting on the mutex */
580     libvlc_lock (p_this->p_libvlc);
581     /* Take the spin again. Note that another thread may have held the
582      * object in the (very short) mean time. */
583     vlc_spin_lock( &internals->ref_spin );
584     b_should_destroy = --internals->i_refcount == 0;
585     vlc_spin_unlock( &internals->ref_spin );
586
587     if( b_should_destroy )
588     {
589         /* Detach from parent to protect against FIND_CHILDREN */
590         parent = p_this->p_parent;
591         if (likely(parent))
592         {
593            /* Unlink */
594            if (internals->prev != NULL)
595                internals->prev->next = internals->next;
596            else
597                vlc_internals(parent)->first = internals->next;
598            if (internals->next != NULL)
599                internals->next->prev = internals->prev;
600         }
601
602         /* We have no children */
603         assert (internals->first == NULL);
604     }
605     libvlc_unlock (p_this->p_libvlc);
606
607     if( b_should_destroy )
608     {
609         int canc;
610
611         canc = vlc_savecancel ();
612         vlc_object_destroy( p_this );
613         vlc_restorecancel (canc);
614         if (parent)
615             vlc_object_release (parent);
616     }
617 }
618
619 #undef vlc_object_attach
620 /**
621  ****************************************************************************
622  * attach object to a parent object
623  *****************************************************************************
624  * This function sets p_this as a child of p_parent, and p_parent as a parent
625  * of p_this.
626  *****************************************************************************/
627 void vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent )
628 {
629     if( !p_this ) return;
630
631     vlc_object_internals_t *pap = vlc_internals (p_parent);
632     vlc_object_internals_t *priv = vlc_internals (p_this);
633
634     priv->prev = NULL;
635     vlc_object_hold (p_parent);
636     libvlc_lock (p_this->p_libvlc);
637
638     /* Attach the parent to its child */
639     assert (p_this->p_parent == NULL);
640     p_this->p_parent = p_parent;
641
642     /* Attach the child to its parent */
643     priv->next = pap->first;
644     if (priv->next != NULL)
645         priv->next->prev = priv;
646     pap->first = priv;
647     libvlc_unlock (p_this->p_libvlc);
648 }
649
650
651 #undef vlc_list_children
652 /**
653  * Gets the list of children of an objects, and increment their reference
654  * count.
655  * @return a list (possibly empty) or NULL in case of error.
656  */
657 vlc_list_t *vlc_list_children( vlc_object_t *obj )
658 {
659     vlc_list_t *l;
660     vlc_object_internals_t *priv;
661     unsigned count = 0;
662
663     libvlc_lock (obj->p_libvlc);
664     for (priv = vlc_internals (obj)->first; priv; priv = priv->next)
665          count++;
666     l = NewList (count);
667     if (likely(l != NULL))
668     {
669         unsigned i = 0;
670
671         for (priv = vlc_internals (obj)->first; priv; priv = priv->next)
672             l->p_values[i++].p_object = vlc_object_hold (vlc_externals (priv));
673     }
674     libvlc_unlock (obj->p_libvlc);
675     return l;
676 }
677
678 static void DumpVariable (const void *data, const VISIT which, const int depth)
679 {
680     if (which != postorder && which != leaf)
681         return;
682     (void) depth;
683
684     const variable_t *p_var = *(const variable_t **)data;
685     const char *psz_type = "unknown";
686
687     switch( p_var->i_type & VLC_VAR_TYPE )
688     {
689 #define MYCASE( type, nice )    \
690         case VLC_VAR_ ## type:  \
691             psz_type = nice;    \
692             break;
693         MYCASE( VOID, "void" );
694         MYCASE( BOOL, "bool" );
695         MYCASE( INTEGER, "integer" );
696         MYCASE( HOTKEY, "hotkey" );
697         MYCASE( STRING, "string" );
698         MYCASE( VARIABLE, "variable" );
699         MYCASE( FLOAT, "float" );
700         MYCASE( TIME, "time" );
701         MYCASE( COORDS, "coords" );
702         MYCASE( ADDRESS, "address" );
703         MYCASE( MUTEX, "mutex" );
704 #undef MYCASE
705     }
706     printf( " *-o \"%s\" (%s", p_var->psz_name, psz_type );
707     if( p_var->psz_text )
708         printf( ", %s", p_var->psz_text );
709     fputc( ')', stdout );
710     if( p_var->i_type & VLC_VAR_HASCHOICE )
711         fputs( ", has choices", stdout );
712     if( p_var->i_type & VLC_VAR_ISCOMMAND )
713         fputs( ", command", stdout );
714     if( p_var->i_entries )
715         printf( ", %d callbacks", p_var->i_entries );
716     switch( p_var->i_type & VLC_VAR_CLASS )
717     {
718         case VLC_VAR_VOID:
719         case VLC_VAR_MUTEX:
720             break;
721         case VLC_VAR_BOOL:
722             printf( ": %s", p_var->val.b_bool ? "true" : "false" );
723             break;
724         case VLC_VAR_INTEGER:
725             printf( ": %"PRId64, p_var->val.i_int );
726             break;
727         case VLC_VAR_STRING:
728             printf( ": \"%s\"", p_var->val.psz_string );
729             break;
730         case VLC_VAR_FLOAT:
731             printf( ": %f", p_var->val.f_float );
732             break;
733         case VLC_VAR_TIME:
734             printf( ": %"PRIi64, (int64_t)p_var->val.i_time );
735             break;
736         case VLC_VAR_COORDS:
737             printf( ": %"PRId32"x%"PRId32,
738                     p_var->val.coords.x, p_var->val.coords.y );
739             break;
740         case VLC_VAR_ADDRESS:
741             printf( ": %p", p_var->val.p_address );
742             break;
743     }
744     fputc( '\n', stdout );
745 }
746
747 /*****************************************************************************
748  * DumpCommand: print the current vlc structure
749  *****************************************************************************
750  * This function prints either an ASCII tree showing the connections between
751  * vlc objects, and additional information such as their refcount, thread ID,
752  * etc. (command "tree"), or the same data as a simple list (command "list").
753  *****************************************************************************/
754 static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd,
755                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
756 {
757     (void)oldval; (void)p_data;
758     vlc_object_t *p_object = NULL;
759
760     if( *newval.psz_string )
761     {
762         /* try using the object's name to find it */
763         p_object = vlc_object_find_name( p_this, newval.psz_string,
764                                          FIND_ANYWHERE );
765         if( !p_object )
766         {
767             return VLC_ENOOBJ;
768         }
769     }
770
771     libvlc_lock (p_this->p_libvlc);
772     if( *psz_cmd == 't' )
773     {
774         char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
775
776         if( !p_object )
777             p_object = VLC_OBJECT(p_this->p_libvlc);
778
779         psz_foo[0] = '|';
780         DumpStructure( vlc_internals(p_object), 0, psz_foo );
781     }
782     else if( *psz_cmd == 'v' )
783     {
784         if( !p_object )
785             p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
786
787         PrintObject( vlc_internals(p_object), "" );
788         vlc_mutex_lock( &vlc_internals( p_object )->var_lock );
789         if( vlc_internals( p_object )->var_root == NULL )
790             puts( " `-o No variables" );
791         else
792             twalk( vlc_internals( p_object )->var_root, DumpVariable );
793         vlc_mutex_unlock( &vlc_internals( p_object )->var_lock );
794     }
795     libvlc_unlock (p_this->p_libvlc);
796
797     if( *newval.psz_string )
798     {
799         vlc_object_release( p_object );
800     }
801     return VLC_SUCCESS;
802 }
803
804 /*****************************************************************************
805  * vlc_list_release: free a list previously allocated by vlc_list_find
806  *****************************************************************************
807  * This function decreases the refcount of all objects in the list and
808  * frees the list.
809  *****************************************************************************/
810 void vlc_list_release( vlc_list_t *p_list )
811 {
812     int i_index;
813
814     for( i_index = 0; i_index < p_list->i_count; i_index++ )
815     {
816         vlc_object_release( p_list->p_values[i_index].p_object );
817     }
818
819     free( p_list->p_values );
820     free( p_list );
821 }
822
823 /* Following functions are local */
824
825 static vlc_object_t *FindParent (vlc_object_t *p_this, int i_type)
826 {
827     for (vlc_object_t *parent = p_this->p_parent;
828          parent != NULL;
829          parent = parent->p_parent)
830     {
831         if (vlc_internals (parent)->i_object_type == i_type)
832             return vlc_object_hold (parent);
833     }
834     return NULL;
835 }
836
837 static vlc_object_t *FindParentName (vlc_object_t *p_this, const char *name)
838 {
839     for (vlc_object_t *parent = p_this->p_parent;
840          parent != NULL;
841          parent = parent->p_parent)
842     {
843         const char *objname = vlc_internals (parent)->psz_name;
844         if (objname && !strcmp (objname, name))
845             return vlc_object_hold (parent);
846     }
847     return NULL;
848 }
849
850 static vlc_object_t *FindChild (vlc_object_internals_t *priv, int i_type)
851 {
852     for (priv = priv->first; priv != NULL; priv = priv->next)
853     {
854         if (priv->i_object_type == i_type)
855             return vlc_object_hold (vlc_externals (priv));
856
857         vlc_object_t *found = FindChild (priv, i_type);
858         if (found != NULL)
859             return found;
860     }
861     return NULL;
862 }
863
864 static vlc_object_t *FindChildName (vlc_object_internals_t *priv,
865                                     const char *name)
866 {
867     for (priv = priv->first; priv != NULL; priv = priv->next)
868     {
869         if (priv->psz_name && !strcmp (priv->psz_name, name))
870             return vlc_object_hold (vlc_externals (priv));
871
872         vlc_object_t *found = FindChildName (priv, name);
873         if (found != NULL)
874             return found;
875     }
876     return NULL;
877 }
878
879 static void PrintObject( vlc_object_internals_t *priv,
880                          const char *psz_prefix )
881 {
882     char psz_refcount[20], psz_thread[30], psz_name[50], psz_parent[20];
883
884     int canc = vlc_savecancel ();
885     memset( &psz_name, 0, sizeof(psz_name) );
886
887     vlc_mutex_lock (&name_lock);
888     if (priv->psz_name != NULL)
889     {
890         snprintf( psz_name, 49, " \"%s\"", priv->psz_name );
891         if( psz_name[48] )
892             psz_name[48] = '\"';
893     }
894     vlc_mutex_unlock (&name_lock);
895
896     psz_refcount[0] = '\0';
897     if( priv->i_refcount > 0 )
898         snprintf( psz_refcount, 19, ", %u refs", priv->i_refcount );
899
900     psz_thread[0] = '\0';
901     if( priv->b_thread )
902         snprintf( psz_thread, 29, " (thread %lu)",
903                   (unsigned long)priv->thread_id );
904
905     psz_parent[0] = '\0';
906     /* FIXME: need structure lock!!! */
907     if( vlc_externals(priv)->p_parent )
908         snprintf( psz_parent, 19, ", parent %p",
909                   vlc_externals(priv)->p_parent );
910
911     printf( " %so %p %s%s%s%s%s\n", psz_prefix,
912             vlc_externals(priv), vlc_externals(priv)->psz_object_type,
913             psz_name, psz_thread, psz_refcount, psz_parent );
914     vlc_restorecancel (canc);
915 }
916
917 static void DumpStructure (vlc_object_internals_t *priv, unsigned i_level,
918                            char *psz_foo)
919 {
920     char i_back = psz_foo[i_level];
921     psz_foo[i_level] = '\0';
922
923     PrintObject (priv, psz_foo);
924
925     psz_foo[i_level] = i_back;
926
927     if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH )
928     {
929         msg_Warn( vlc_externals(priv), "structure tree is too deep" );
930         return;
931     }
932
933     for (priv = priv->first; priv != NULL; priv = priv->next)
934     {
935         if( i_level )
936         {
937             psz_foo[i_level-1] = ' ';
938
939             if( psz_foo[i_level-2] == '`' )
940             {
941                 psz_foo[i_level-2] = ' ';
942             }
943         }
944
945         psz_foo[i_level] = priv->next ? '|' : '`';
946         psz_foo[i_level+1] = '-';
947         psz_foo[i_level+2] = '\0';
948
949         DumpStructure (priv, i_level + 2, psz_foo);
950     }
951 }
952
953 static vlc_list_t * NewList( int i_count )
954 {
955     vlc_list_t * p_list = malloc( sizeof( vlc_list_t ) );
956     if( p_list == NULL )
957         return NULL;
958
959     p_list->i_count = i_count;
960
961     if( i_count == 0 )
962     {
963         p_list->p_values = NULL;
964         return p_list;
965     }
966
967     p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) );
968     if( p_list->p_values == NULL )
969     {
970         p_list->i_count = 0;
971         return p_list;
972     }
973
974     return p_list;
975 }