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