1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2003 the VideoLAN team
7 * Authors: Cyril Deguet <asmax@via.ecp.fr>
8 * Olivier Teulière <ipkiss@via.ecp.fr>
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 #include "theme_loader.hpp"
27 #include "../parser/builder.hpp"
28 #include "../parser/skin_parser.hpp"
29 #include "../src/os_factory.hpp"
30 #include "../src/vlcproc.hpp"
31 #include "../src/window_manager.hpp"
36 #ifdef HAVE_SYS_STAT_H
37 # include <sys/stat.h>
41 #elif defined( WIN32 ) && !defined( UNDER_CE )
50 #if defined( HAVE_ZLIB_H )
53 int gzopen_frontend ( char *pathname, int oflags, int mode );
54 int gzclose_frontend( int );
55 int gzread_frontend ( int, void *, size_t );
56 int gzwrite_frontend( int, const void *, size_t );
57 #if defined( HAVE_LIBTAR_H )
61 int tar_open ( TAR **t, char *pathname, int oflags );
62 int tar_extract_all ( TAR *t, char *prefix );
63 int tar_close ( TAR *t );
67 #define DEFAULT_XML_FILE "theme.xml"
70 bool ThemeLoader::load( const string &fileName )
72 // First, we try to un-targz the file, and if it fails we hope it's a XML
74 #if defined( HAVE_ZLIB_H )
75 if( ! extract( fileName ) && ! parse( fileName ) )
78 if( ! parse( fileName ) )
82 Theme *pNewTheme = getIntf()->p_sys->p_theme;
88 // Check if the skin to load is in the config file, to load its config
89 char *skin_last = config_GetPsz( getIntf(), "skins2-last" );
90 if( skin_last != NULL && fileName == (string)skin_last )
92 // Restore the theme configuration
93 getIntf()->p_sys->p_theme->loadConfig();
94 // Used to anchor the windows at the beginning
95 pNewTheme->getWindowManager().stopMove();
99 config_PutPsz( getIntf(), "skins2-last", fileName.c_str() );
101 pNewTheme->getWindowManager().showAll();
103 if( skin_last ) free( skin_last );
105 // The new theme cannot embed a video output yet
106 VlcProc::instance( getIntf() )->dropVout();
112 #if defined( HAVE_ZLIB_H )
113 bool ThemeLoader::extractTarGz( const string &tarFile, const string &rootDir )
116 #if defined( HAVE_LIBTAR_H )
117 tartype_t gztype = { (openfunc_t) gzopen_frontend,
118 (closefunc_t) gzclose_frontend,
119 (readfunc_t) gzread_frontend,
120 (writefunc_t) gzwrite_frontend };
122 if( tar_open( &t, (char *)tarFile.c_str(), &gztype, O_RDONLY, 0,
125 if( tar_open( &t, (char *)tarFile.c_str(), O_RDONLY ) == -1 )
131 if( tar_extract_all( t, (char *)rootDir.c_str() ) != 0 )
137 if( tar_close( t ) != 0 )
146 bool ThemeLoader::extract( const string &fileName )
148 char *tmpdir = tempnam( NULL, "vlt" );
149 string tempPath = tmpdir;
152 // Extract the file in a temporary directory
153 if( ! extractTarGz( fileName, tempPath ) )
156 // Find the XML file and parse it
158 if( ! findThemeFile( tempPath, xmlFile ) || ! parse( xmlFile ) )
160 msg_Err( getIntf(), "%s doesn't contain a " DEFAULT_XML_FILE " file",
162 deleteTempFiles( tempPath );
167 deleteTempFiles( tempPath );
172 void ThemeLoader::deleteTempFiles( const string &path )
174 OSFactory::instance( getIntf() )->rmDir( path );
176 #endif // HAVE_ZLIB_H
179 bool ThemeLoader::parse( const string &xmlFile )
182 msg_Dbg( getIntf(), "Using skin file: %s", xmlFile.c_str() );
184 // Extract the path of the XML file
186 const string &sep = OSFactory::instance( getIntf() )->getDirSeparator();
187 string::size_type p = xmlFile.rfind( sep, xmlFile.size() );
188 if( p != string::npos )
190 path = xmlFile.substr( 0, p + 1 );
198 SkinParser parser( getIntf(), xmlFile, path );
199 if( ! parser.parse() )
201 msg_Err( getIntf(), "Failed to parse %s", xmlFile.c_str() );
205 // Build and store the theme
206 Builder builder( getIntf(), parser.getData() );
207 getIntf()->p_sys->p_theme = builder.build();
213 bool ThemeLoader::findThemeFile( const string &rootDir, string &themeFilePath )
216 const string &sep = OSFactory::instance( getIntf() )->getDirSeparator();
219 struct dirent *pDirContent;
222 pCurrDir = opendir( rootDir.c_str() );
224 if( pCurrDir == NULL )
227 msg_Dbg( getIntf(), "Cannot open directory %s", rootDir.c_str() );
231 // Get the first directory entry
232 pDirContent = (dirent*)readdir( pCurrDir );
234 // While we still have entries in the directory
235 while( pDirContent != NULL )
237 string newURI = rootDir + sep + pDirContent->d_name;
240 if( string( pDirContent->d_name ) != "." &&
241 string( pDirContent->d_name ) != ".." )
243 #if defined( S_ISDIR )
244 struct stat stat_data;
245 stat( newURI.c_str(), &stat_data );
246 if( S_ISDIR(stat_data.st_mode) )
247 #elif defined( DT_DIR )
248 if( pDirContent->d_type & DT_DIR )
253 // Can we find the theme file in this subdirectory?
254 if( findThemeFile( newURI, themeFilePath ) )
256 closedir( pCurrDir );
262 // Found the theme file?
263 if( string( DEFAULT_XML_FILE ) ==
264 string( pDirContent->d_name ) )
266 themeFilePath = newURI;
267 closedir( pCurrDir );
273 pDirContent = (dirent*)readdir( pCurrDir );
276 closedir( pCurrDir );
281 #if !defined( HAVE_LIBTAR_H ) && defined( HAVE_ZLIB_H )
284 # define mkdir(dirname,mode) _mkdir(dirname)
287 /* Values used in typeflag field */
288 #define REGTYPE '0' /* regular file */
289 #define AREGTYPE '\0' /* regular file */
290 #define DIRTYPE '5' /* directory */
292 #define BLOCKSIZE 512
296 char name[100]; /* 0 */
297 char mode[8]; /* 100 */
298 char uid[8]; /* 108 */
299 char gid[8]; /* 116 */
300 char size[12]; /* 124 */
301 char mtime[12]; /* 136 */
302 char chksum[8]; /* 148 */
303 char typeflag; /* 156 */
304 char linkname[100]; /* 157 */
305 char magic[6]; /* 257 */
306 char version[2]; /* 263 */
307 char uname[32]; /* 265 */
308 char gname[32]; /* 297 */
309 char devmajor[8]; /* 329 */
310 char devminor[8]; /* 337 */
311 char prefix[155]; /* 345 */
317 char buffer[BLOCKSIZE];
318 struct tar_header header;
322 /* helper functions */
323 int getoct( char *p, int width );
324 int makedir( char *newdir );
326 int tar_open( TAR **t, char *pathname, int oflags )
328 gzFile f = gzopen( pathname, "rb" );
331 fprintf( stderr, "Couldn't gzopen %s\n", pathname );
335 *t = (gzFile *)malloc( sizeof(gzFile) );
341 int tar_extract_all( TAR *t, char *prefix )
343 union tar_buffer buffer;
344 int len, err, getheader = 1, remaining = 0;
345 FILE *outfile = NULL;
346 char fname[BLOCKSIZE + PATH_MAX];
350 len = gzread( *t, &buffer, BLOCKSIZE );
353 fprintf( stderr, "%s\n", gzerror(*t, &err) );
357 * Always expect complete blocks to process
358 * the tar information.
360 if( len != 0 && len != BLOCKSIZE )
362 fprintf( stderr, "gzread: incomplete block read\n" );
367 * If we have to get a tar header
372 * If we met the end of the tar
373 * or the end-of-tar block, we are done
375 if( (len == 0) || (buffer.header.name[0] == 0) )
380 sprintf( fname, "%s/%s", prefix, buffer.header.name );
382 /* Check magic value in header */
383 if( strncmp( buffer.header.magic, "GNUtar", 6 ) &&
384 strncmp( buffer.header.magic, "ustar", 5 ) )
386 //fprintf(stderr, "not a tar file\n");
390 switch( buffer.header.typeflag )
397 remaining = getoct( buffer.header.size, 12 );
400 outfile = fopen( fname, "wb" );
401 if( outfile == NULL )
403 /* try creating directory */
404 char *p = strrchr( fname, '/' );
410 outfile = fopen( fname, "wb" );
413 fprintf( stderr, "tar couldn't create %s\n",
422 * could have no contents
424 getheader = (remaining) ? 0 : 1;
432 unsigned int bytes = (remaining > BLOCKSIZE)?BLOCKSIZE:remaining;
434 if( outfile != NULL )
436 if( fwrite( &buffer, sizeof(char), bytes, outfile ) != bytes )
438 fprintf( stderr, "error writing %s skipping...\n", fname );
447 if( outfile != NULL )
460 int tar_close( TAR *t )
462 if( gzclose( *t ) != Z_OK ) fprintf( stderr, "failed gzclose\n" );
468 /* helper functions */
469 int getoct( char *p, int width )
481 result = result * 8 + (c - '0');
487 /* Recursive make directory
488 * Abort if you get an ENOENT errno somewhere in the middle
489 * e.g. ignore error "mkdir on existing directory"
491 * return 1 if OK, 0 on error
493 int makedir( char *newdir )
495 char *p, *buffer = strdup( newdir );
496 int len = strlen( buffer );
504 if( buffer[len-1] == '/' )
506 buffer[len-1] = '\0';
509 if( mkdir( buffer, 0775 ) == 0 )
520 while( *p && *p != '\\' && *p != '/' ) p++;
523 if( ( mkdir( buffer, 0775 ) == -1 ) && ( errno == ENOENT ) )
525 fprintf( stderr, "couldn't create directory %s\n", buffer );
529 if( hold == 0 ) break;
539 static int currentGzFd = -1;
540 static void * currentGzVp = NULL;
542 int gzopen_frontend( char *pathname, int oflags, int mode )
561 gzf = gzopen( pathname, gzflags );
575 int gzclose_frontend( int fd )
577 if( currentGzVp != NULL && fd != -1 )
579 return gzclose( currentGzVp );
584 int gzread_frontend( int fd, void *p_buffer, size_t i_length )
586 if( currentGzVp != NULL && fd != -1 )
588 return gzread( currentGzVp, p_buffer, i_length );
593 int gzwrite_frontend( int fd, const void * p_buffer, size_t i_length )
595 if( currentGzVp != NULL && fd != -1 )
597 return gzwrite( currentGzVp, p_buffer, i_length );