directory.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:15k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * directory.c: expands a directory (directory: access plug-in)
  3.  *****************************************************************************
  4.  * Copyright (C) 2002-2008 the VideoLAN team
  5.  * $Id: dd238e510d2b5b2db3c29cdc85502ce28e65d47b $
  6.  *
  7.  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
  8.  *          Rémi Denis-Courmont
  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.  * Preamble
  26.  *****************************************************************************/
  27. #ifdef HAVE_CONFIG_H
  28. # include "config.h"
  29. #endif
  30. #include <vlc_common.h>
  31. #include <vlc_plugin.h>
  32. #include <vlc_access.h>
  33. #ifdef HAVE_SYS_TYPES_H
  34. #   include <sys/types.h>
  35. #endif
  36. #ifdef HAVE_SYS_STAT_H
  37. #   include <sys/stat.h>
  38. #endif
  39. #ifdef HAVE_UNISTD_H
  40. #   include <unistd.h>
  41. #elif defined( WIN32 ) && !defined( UNDER_CE )
  42. #   include <io.h>
  43. #endif
  44. #ifdef HAVE_DIRENT_H
  45. #   include <dirent.h>
  46. #endif
  47. #ifdef __sun__
  48. static inline int dirfd (DIR *dir)
  49. {
  50.     return dir->dd_fd;
  51. }
  52. #endif
  53. #include <vlc_charset.h>
  54. #include <vlc_url.h>
  55. #include <vlc_strings.h>
  56. /*****************************************************************************
  57.  * Module descriptor
  58.  *****************************************************************************/
  59. static int  Open ( vlc_object_t * );
  60. static void Close( vlc_object_t * );
  61. #define RECURSIVE_TEXT N_("Subdirectory behavior")
  62. #define RECURSIVE_LONGTEXT N_( 
  63.         "Select whether subdirectories must be expanded.n" 
  64.         "none: subdirectories do not appear in the playlist.n" 
  65.         "collapse: subdirectories appear but are expanded on first play.n" 
  66.         "expand: all subdirectories are expanded.n" )
  67. static const char *const psz_recursive_list[] = { "none", "collapse", "expand" };
  68. static const char *const psz_recursive_list_text[] = {
  69.     N_("none"), N_("collapse"), N_("expand") };
  70. #define IGNORE_TEXT N_("Ignored extensions")
  71. #define IGNORE_LONGTEXT N_( 
  72.         "Files with these extensions will not be added to playlist when " 
  73.         "opening a directory.n" 
  74.         "This is useful if you add directories that contain playlist files " 
  75.         "for instance. Use a comma-separated list of extensions." )
  76. vlc_module_begin ()
  77.     set_category( CAT_INPUT )
  78.     set_shortname( N_("Directory" ) )
  79.     set_subcategory( SUBCAT_INPUT_ACCESS )
  80.     set_description( N_("Standard filesystem directory input") )
  81.     set_capability( "access", 55 )
  82.     add_shortcut( "directory" )
  83.     add_shortcut( "dir" )
  84.     add_shortcut( "file" )
  85.     add_string( "recursive", "expand" , NULL, RECURSIVE_TEXT,
  86.                 RECURSIVE_LONGTEXT, false )
  87.       change_string_list( psz_recursive_list, psz_recursive_list_text, 0 )
  88.     add_string( "ignore-filetypes", "m3u,db,nfo,ini,jpg,jpeg,ljpg,gif,png,pgm,pgmyuv,pbm,pam,tga,bmp,pnm,xpm,xcf,pcx,tif,tiff,lbm,sfv,txt,sub,idx,srt,cue,ssa",
  89.                 NULL, IGNORE_TEXT, IGNORE_LONGTEXT, false )
  90.     set_callbacks( Open, Close )
  91. vlc_module_end ()
  92. /*****************************************************************************
  93.  * Local prototypes, constants, structures
  94.  *****************************************************************************/
  95. enum
  96. {
  97.     MODE_EXPAND,
  98.     MODE_COLLAPSE,
  99.     MODE_NONE
  100. };
  101. typedef struct directory_t directory_t;
  102. struct directory_t
  103. {
  104.     directory_t *parent;
  105.     DIR         *handle;
  106.     char        *uri;
  107. #ifndef WIN32
  108.     struct stat  st;
  109. #endif
  110.     char         path[1];
  111. };
  112. struct access_sys_t
  113. {
  114.     directory_t *current;
  115.     DIR *handle;
  116.     char *ignored_exts;
  117.     int mode;
  118.     int i_item_count;
  119.     char *psz_xspf_extension;
  120. };
  121. static block_t *Block( access_t * );
  122. static int Control( access_t *, int, va_list );
  123. /*****************************************************************************
  124.  * Open: open the directory
  125.  *****************************************************************************/
  126. static int Open( vlc_object_t *p_this )
  127. {
  128.     access_t *p_access = (access_t*)p_this;
  129.     access_sys_t *p_sys;
  130.     if( !p_access->psz_path )
  131.         return VLC_EGENERIC;
  132.     DIR *handle;
  133.     if (strcmp (p_access->psz_path, "-"))
  134.         handle = utf8_opendir (p_access->psz_path);
  135.     else
  136.     {
  137. #if 0   /* This won't work yet, it generates paths like "-/music.ogg".
  138.          * We'd need to use openat() here and in the file access... */
  139.         int fd = dup (0);
  140.         handle = fdopendir (fd);
  141.         if (handle == NULL)
  142.             close (fd);
  143. #else
  144.         return VLC_EGENERIC;
  145. #endif
  146.     }
  147.     if (handle == NULL)
  148.         return VLC_EGENERIC;
  149.     p_sys = malloc (sizeof (*p_sys));
  150.     if (!p_sys)
  151.     {
  152.         closedir( handle );
  153.         return VLC_ENOMEM;
  154.     }
  155.     p_access->p_sys = p_sys;
  156.     p_sys->current = NULL;
  157.     p_sys->handle = handle;
  158.     p_sys->ignored_exts = var_CreateGetString (p_access, "ignore-filetypes");
  159.     p_sys->i_item_count = 0;
  160.     p_sys->psz_xspf_extension = strdup( "" );
  161.     /* Handle mode */
  162.     char *psz = var_CreateGetString( p_access, "recursive" );
  163.     if( *psz == '' || !strcasecmp( psz, "none" )  )
  164.         p_sys->mode = MODE_NONE;
  165.     else if( !strcasecmp( psz, "collapse" )  )
  166.         p_sys->mode = MODE_COLLAPSE;
  167.     else
  168.         p_sys->mode = MODE_EXPAND;
  169.     free( psz );
  170.     p_access->pf_read  = NULL;
  171.     p_access->pf_block = Block;
  172.     p_access->pf_seek  = NULL;
  173.     p_access->pf_control= Control;
  174.     free (p_access->psz_demux);
  175.     p_access->psz_demux = strdup ("xspf-open");
  176.     return VLC_SUCCESS;
  177. }
  178. /*****************************************************************************
  179.  * Close: close the target
  180.  *****************************************************************************/
  181. static void Close( vlc_object_t * p_this )
  182. {
  183.     access_t *p_access = (access_t*)p_this;
  184.     access_sys_t *p_sys = p_access->p_sys;
  185.     while (p_sys->current)
  186.     {
  187.         directory_t *current = p_sys->current;
  188.         p_sys->current = current->parent;
  189.         closedir (current->handle);
  190.         free (current->uri);
  191.         free (current);
  192.     }
  193.     if (p_sys->handle != NULL)
  194.         closedir (p_sys->handle); /* corner case,:Block() not called ever */
  195.     free (p_sys->psz_xspf_extension);
  196.     free (p_sys->ignored_exts);
  197.     free (p_sys);
  198. }
  199. /* Detect directories that recurse into themselves. */
  200. static bool has_inode_loop (const directory_t *dir)
  201. {
  202. #ifndef WIN32
  203.     dev_t dev = dir->st.st_dev;
  204.     ino_t inode = dir->st.st_ino;
  205.     while ((dir = dir->parent) != NULL)
  206.         if ((dir->st.st_dev == dev) && (dir->st.st_ino == inode))
  207.             return true;
  208. #else
  209. # define fstat( fd, st ) (0)
  210. #endif
  211.     return false;
  212. }
  213. static block_t *Block (access_t *p_access)
  214. {
  215.     access_sys_t *p_sys = p_access->p_sys;
  216.     directory_t *current = p_sys->current;
  217.     if (p_access->info.b_eof)
  218.         return NULL;
  219.     if (current == NULL)
  220.     {   /* Startup: send the XSPF header */
  221.         static const char header[] =
  222.             "<?xml version="1.0" encoding="UTF-8"?>n"
  223.             "<playlist version="1" xmlns="http://xspf.org/ns/0/" xmlns:vlc="http://www.videolan.org/vlc/playlist/ns/0/">n"
  224.             " <trackList>n";
  225.         block_t *block = block_Alloc (sizeof (header) - 1);
  226.         if (!block)
  227.             goto fatal;
  228.         memcpy (block->p_buffer, header, sizeof (header) - 1);
  229.         /* "Open" the base directory */
  230.         current = malloc (sizeof (*current) + strlen (p_access->psz_path));
  231.         if (current == NULL)
  232.         {
  233.             block_Release (block);
  234.             goto fatal;
  235.         }
  236.         current->parent = NULL;
  237.         current->handle = p_sys->handle;
  238.         strcpy (current->path, p_access->psz_path);
  239.         current->uri = make_URI (current->path);
  240.         if ((current->uri == NULL)
  241.          || fstat (dirfd (current->handle), &current->st))
  242.         {
  243.             free (current->uri);
  244.             free (current);
  245.             block_Release (block);
  246.             goto fatal;
  247.         }
  248.         p_sys->handle = NULL;
  249.         p_sys->current = current;
  250.         return block;
  251.     }
  252.     char *entry = utf8_readdir (current->handle);
  253.     if (entry == NULL)
  254.     {   /* End of directory, go back to parent */
  255.         closedir (current->handle);
  256.         p_sys->current = current->parent;
  257.         free (current->uri);
  258.         free (current);
  259.         if (p_sys->current == NULL)
  260.         {   /* End of XSPF playlist */
  261.             char *footer;
  262.             int len = asprintf( &footer, " </trackList>n" 
  263.                 " <extension application="http://www.videolan.org/vlc/playlist/0">n" 
  264.                 "%s" 
  265.                 " </extension>n" 
  266.                 "</playlist>n", p_sys->psz_xspf_extension );
  267.             if( len < 0 )
  268.                 goto fatal;
  269.             block_t *block = block_Alloc ( len );
  270.             if (!block)
  271.                 goto fatal;
  272.             memcpy (block->p_buffer, footer, len);
  273.             free( footer );
  274.             p_access->info.b_eof = true;
  275.             return block;
  276.         }
  277.         else
  278.         {
  279.             /* This was the end of a "subnode" */
  280.             /* Write the ID to the extension */
  281.             char *old_xspf_extension = p_sys->psz_xspf_extension;
  282.             if (old_xspf_extension == NULL)
  283.                 goto fatal;
  284.             int len2 = asprintf( &p_sys->psz_xspf_extension, "%s  </vlc:node>n", old_xspf_extension );
  285.             if (len2 == -1)
  286.                 goto fatal;
  287.             free( old_xspf_extension );
  288.         }
  289.         return NULL;
  290.     }
  291.     /* Skip current, parent and hidden directories */
  292.     if (entry[0] == '.')
  293.     {
  294.         free (entry);
  295.         return NULL;
  296.     }
  297.     /* Handle recursion */
  298.     if (p_sys->mode != MODE_COLLAPSE)
  299.     {
  300.         directory_t *sub = malloc (sizeof (*sub) + strlen (current->path) + 1
  301.                                                  + strlen (entry));
  302.         if (sub == NULL)
  303.         {
  304.             free (entry);
  305.             return NULL;
  306.         }
  307.         sprintf (sub->path, "%s/%s", current->path, entry);
  308.         DIR *handle = utf8_opendir (sub->path);
  309.         if (handle != NULL)
  310.         {
  311.             sub->parent = current;
  312.             sub->handle = handle;
  313.             char *encoded = encode_URI_component (entry);
  314.             if ((encoded == NULL)
  315.              || (asprintf (&sub->uri, "%s/%s", current->uri, encoded) == -1))
  316.                  sub->uri = NULL;
  317.             free (encoded);
  318.             if ((p_sys->mode == MODE_NONE)
  319.              || fstat (dirfd (handle), &sub->st)
  320.              || has_inode_loop (sub)
  321.              || (sub->uri == NULL))
  322.             {
  323.                 free (entry);
  324.                 closedir (handle);
  325.                 free (sub->uri);
  326.                 free (sub);
  327.                 return NULL;
  328.             }
  329.             p_sys->current = sub;
  330.             /* Add node to xspf extension */
  331.             char *old_xspf_extension = p_sys->psz_xspf_extension;
  332.             if (old_xspf_extension == NULL)
  333.             {
  334.                 free (entry);
  335.                 goto fatal;
  336.             }
  337.             char *title = convert_xml_special_chars (entry);
  338.             free (entry);
  339.             if (title == NULL
  340.              || asprintf (&p_sys->psz_xspf_extension, "%s"
  341.                           "  <vlc:node title="%s">n", old_xspf_extension,
  342.                           title) == -1)
  343.             {
  344.                 free (title);
  345.                 goto fatal;
  346.             }
  347.             free (title);
  348.             free (old_xspf_extension);
  349.             return NULL;
  350.         }
  351.         else
  352.             free (sub);
  353.     }
  354.     /* Skip files with ignored extensions */
  355.     if (p_sys->ignored_exts != NULL)
  356.     {
  357.         const char *ext = strrchr (entry, '.');
  358.         if (ext != NULL)
  359.         {
  360.             size_t extlen = strlen (++ext);
  361.             for (const char *type = p_sys->ignored_exts, *end;
  362.                  type[0]; type = end + 1)
  363.             {
  364.                 end = strchr (type, ',');
  365.                 if (end == NULL)
  366.                     end = type + strlen (type);
  367.                 if (type + extlen == end
  368.                  && !strncasecmp (ext, type, extlen))
  369.                 {
  370.                     free (entry);
  371.                     return NULL;
  372.                 }
  373.                 if (*end == '')
  374.                     break;
  375.             }
  376.         }
  377.     }
  378.     char *encoded = encode_URI_component (entry);
  379.     free (entry);
  380.     if (encoded == NULL)
  381.         goto fatal;
  382.     int len = asprintf (&entry,
  383.                         "  <track><location>%s/%s</location>n" 
  384.                         "   <extension application="http://www.videolan.org/vlc/playlist/0">n" 
  385.                         "    <vlc:id>%d</vlc:id>n" 
  386.                         "   </extension>n" 
  387.                         "  </track>n",
  388.                         current->uri, encoded, p_sys->i_item_count++);
  389.     free (encoded);
  390.     if (len == -1)
  391.         goto fatal;
  392.     /* Write the ID to the extension */
  393.     char *old_xspf_extension = p_sys->psz_xspf_extension;
  394.     if (old_xspf_extension == NULL)
  395.         goto fatal;
  396.     int len2 = asprintf( &p_sys->psz_xspf_extension, "%s   <vlc:item tid="%i" />n",
  397.                             old_xspf_extension, p_sys->i_item_count-1 );
  398.     if (len2 == -1)
  399.         goto fatal;
  400.     free( old_xspf_extension );
  401.     /* TODO: new block allocator for malloc()ated data */
  402.     block_t *block = block_Alloc (len);
  403.     if (!block)
  404.     {
  405.         free (entry);
  406.         goto fatal;
  407.     }
  408.     memcpy (block->p_buffer, entry, len);
  409.     free (entry);
  410.     return block;
  411. fatal:
  412.     p_access->info.b_eof = true;
  413.     return NULL;
  414. }
  415. /*****************************************************************************
  416.  * Control:
  417.  *****************************************************************************/
  418. static int Control( access_t *p_access, int i_query, va_list args )
  419. {
  420.     bool    *pb_bool;
  421.     int64_t *pi_64;
  422.     switch( i_query )
  423.     {
  424.         /* */
  425.         case ACCESS_CAN_SEEK:
  426.         case ACCESS_CAN_FASTSEEK:
  427.             pb_bool = (bool*)va_arg( args, bool* );
  428.             *pb_bool = false;
  429.             break;
  430.         case ACCESS_CAN_PAUSE:
  431.         case ACCESS_CAN_CONTROL_PACE:
  432.             pb_bool = (bool*)va_arg( args, bool* );
  433.             *pb_bool = true;
  434.             break;
  435.         /* */
  436.         case ACCESS_GET_PTS_DELAY:
  437.             pi_64 = (int64_t*)va_arg( args, int64_t * );
  438.             *pi_64 = DEFAULT_PTS_DELAY * 1000;
  439.             break;
  440.         /* */
  441.         case ACCESS_SET_PAUSE_STATE:
  442.         case ACCESS_GET_TITLE_INFO:
  443.         case ACCESS_SET_TITLE:
  444.         case ACCESS_SET_SEEKPOINT:
  445.         case ACCESS_SET_PRIVATE_ID_STATE:
  446.         case ACCESS_GET_CONTENT_TYPE:
  447.         case ACCESS_GET_META:
  448.             return VLC_EGENERIC;
  449.         default:
  450.             msg_Warn( p_access, "unimplemented query in control" );
  451.             return VLC_EGENERIC;
  452.     }
  453.     return VLC_SUCCESS;
  454. }