Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

theme_loader.cpp

00001 /*****************************************************************************
00002  * theme_loader.cpp
00003  *****************************************************************************
00004  * Copyright (C) 2003 the VideoLAN team
00005  * $Id: theme_loader.cpp 12912 2005-10-22 11:57:29Z asmax $
00006  *
00007  * Authors: Cyril Deguet     <[email protected]>
00008  *          Olivier Teulière <[email protected]>
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00023  *****************************************************************************/
00024 
00025 #include "theme_loader.hpp"
00026 #include "theme.hpp"
00027 #include "../parser/builder.hpp"
00028 #include "../parser/skin_parser.hpp"
00029 #include "../src/os_factory.hpp"
00030 #include "../src/vlcproc.hpp"
00031 #include "../src/window_manager.hpp"
00032 
00033 #ifdef HAVE_FCNTL_H
00034 #   include <fcntl.h>
00035 #endif
00036 #ifdef HAVE_SYS_STAT_H
00037 #   include <sys/stat.h>
00038 #endif
00039 #ifdef HAVE_UNISTD_H
00040 #   include <unistd.h>
00041 #elif defined( WIN32 ) && !defined( UNDER_CE )
00042 #   include <direct.h>
00043 #endif
00044 
00045 #ifdef HAVE_DIRENT_H
00046 #   include <dirent.h>
00047 #endif
00048 
00049 
00050 #if defined( HAVE_ZLIB_H )
00051 #   include <zlib.h>
00052 #   include <errno.h>
00053 int gzopen_frontend ( char *pathname, int oflags, int mode );
00054 int gzclose_frontend( int );
00055 int gzread_frontend ( int, void *, size_t );
00056 int gzwrite_frontend( int, const void *, size_t );
00057 #if defined( HAVE_LIBTAR_H )
00058 #   include <libtar.h>
00059 #else
00060 typedef gzFile TAR;
00061 int tar_open        ( TAR **t, char *pathname, int oflags );
00062 int tar_extract_all ( TAR *t, char *prefix );
00063 int tar_close       ( TAR *t );
00064 #endif
00065 #endif
00066 
00067 #define DEFAULT_XML_FILE "theme.xml"
00068 
00069 
00070 bool ThemeLoader::load( const string &fileName )
00071 {
00072     // First, we try to un-targz the file, and if it fails we hope it's a XML
00073     // file...
00074 #if defined( HAVE_ZLIB_H )
00075     if( ! extract( fileName ) && ! parse( fileName ) )
00076         return false;
00077 #else
00078     if( ! parse( fileName ) )
00079         return false;
00080 #endif
00081 
00082     Theme *pNewTheme = getIntf()->p_sys->p_theme;
00083     if( !pNewTheme )
00084     {
00085         return false;
00086     }
00087 
00088     // Check if the skin to load is in the config file, to load its config
00089     char *skin_last = config_GetPsz( getIntf(), "skins2-last" );
00090     if( skin_last != NULL && fileName == (string)skin_last )
00091     {
00092         // Restore the theme configuration
00093         getIntf()->p_sys->p_theme->loadConfig();
00094         // Used to anchor the windows at the beginning
00095         pNewTheme->getWindowManager().stopMove();
00096     }
00097     else
00098     {
00099         config_PutPsz( getIntf(), "skins2-last", fileName.c_str() );
00100         // Show the windows
00101         pNewTheme->getWindowManager().showAll( true );
00102     }
00103     if( skin_last ) free( skin_last );
00104 
00105     // The new theme cannot embed a video output yet
00106     VlcProc::instance( getIntf() )->dropVout();
00107 
00108     return true;
00109 }
00110 
00111 
00112 #if defined( HAVE_ZLIB_H )
00113 bool ThemeLoader::extractTarGz( const string &tarFile, const string &rootDir )
00114 {
00115     TAR *t;
00116 #if defined( HAVE_LIBTAR_H )
00117     tartype_t gztype = { (openfunc_t) gzopen_frontend,
00118                          (closefunc_t) gzclose_frontend,
00119                          (readfunc_t) gzread_frontend,
00120                          (writefunc_t) gzwrite_frontend };
00121 
00122     if( tar_open( &t, (char *)tarFile.c_str(), &gztype, O_RDONLY, 0,
00123                   TAR_GNU ) == -1 )
00124 #else
00125     if( tar_open( &t, (char *)tarFile.c_str(), O_RDONLY ) == -1 )
00126 #endif
00127     {
00128         return false;
00129     }
00130 
00131     if( tar_extract_all( t, (char *)rootDir.c_str() ) != 0 )
00132     {
00133         tar_close( t );
00134         return false;
00135     }
00136 
00137     if( tar_close( t ) != 0 )
00138     {
00139         return false;
00140     }
00141 
00142     return true;
00143 }
00144 
00145 
00146 bool ThemeLoader::extract( const string &fileName )
00147 {
00148     char *tmpdir = tempnam( NULL, "vlt" );
00149     string tempPath = tmpdir;
00150     free( tmpdir );
00151 
00152     // Extract the file in a temporary directory
00153     if( ! extractTarGz( fileName, tempPath ) )
00154         return false;
00155 
00156     // Find the XML file and parse it
00157     string xmlFile;
00158     if( ! findThemeFile( tempPath, xmlFile ) || ! parse( xmlFile ) )
00159     {
00160         msg_Err( getIntf(), "%s doesn't contain a " DEFAULT_XML_FILE " file",
00161                  fileName.c_str() );
00162         deleteTempFiles( tempPath );
00163         return false;
00164     }
00165 
00166     // Clean-up
00167     deleteTempFiles( tempPath );
00168     return true;
00169 }
00170 
00171 
00172 void ThemeLoader::deleteTempFiles( const string &path )
00173 {
00174     OSFactory::instance( getIntf() )->rmDir( path );
00175 }
00176 #endif // HAVE_ZLIB_H
00177 
00178 
00179 bool ThemeLoader::parse( const string &xmlFile )
00180 {
00181     // File loaded
00182     msg_Dbg( getIntf(), "Using skin file: %s", xmlFile.c_str() );
00183 
00184     // Extract the path of the XML file
00185     string path;
00186     const string &sep = OSFactory::instance( getIntf() )->getDirSeparator();
00187     string::size_type p = xmlFile.rfind( sep, xmlFile.size() );
00188     if( p != string::npos )
00189     {
00190         path = xmlFile.substr( 0, p + 1 );
00191     }
00192     else
00193     {
00194         path = "";
00195     }
00196 
00197     // Start the parser
00198     SkinParser parser( getIntf(), xmlFile, path );
00199     if( ! parser.parse() )
00200     {
00201         msg_Err( getIntf(), "Failed to parse %s", xmlFile.c_str() );
00202         return false;
00203     }
00204 
00205     // Build and store the theme
00206     Builder builder( getIntf(), parser.getData() );
00207     getIntf()->p_sys->p_theme = builder.build();
00208 
00209     return true;
00210 }
00211 
00212 
00213 bool ThemeLoader::findThemeFile( const string &rootDir, string &themeFilePath )
00214 {
00215     // Path separator
00216     const string &sep = OSFactory::instance( getIntf() )->getDirSeparator();
00217 
00218     DIR *pCurrDir;
00219     struct dirent *pDirContent;
00220 
00221     // Open the dir
00222     pCurrDir = opendir( rootDir.c_str() );
00223 
00224     if( pCurrDir == NULL )
00225     {
00226         // An error occurred
00227         msg_Dbg( getIntf(), "Cannot open directory %s", rootDir.c_str() );
00228         return false;
00229     }
00230 
00231     // Get the first directory entry
00232     pDirContent = (dirent*)readdir( pCurrDir );
00233 
00234     // While we still have entries in the directory
00235     while( pDirContent != NULL )
00236     {
00237         string newURI = rootDir + sep + pDirContent->d_name;
00238 
00239         // Skip . and ..
00240         if( string( pDirContent->d_name ) != "." &&
00241             string( pDirContent->d_name ) != ".." )
00242         {
00243 #if defined( S_ISDIR )
00244             struct stat stat_data;
00245             stat( newURI.c_str(), &stat_data );
00246             if( S_ISDIR(stat_data.st_mode) )
00247 #elif defined( DT_DIR )
00248             if( pDirContent->d_type & DT_DIR )
00249 #else
00250             if( 0 )
00251 #endif
00252             {
00253                 // Can we find the theme file in this subdirectory?
00254                 if( findThemeFile( newURI, themeFilePath ) )
00255                 {
00256                     closedir( pCurrDir );
00257                     return true;
00258                 }
00259             }
00260             else
00261             {
00262                 // Found the theme file?
00263                 if( string( DEFAULT_XML_FILE ) ==
00264                     string( pDirContent->d_name ) )
00265                 {
00266                     themeFilePath = newURI;
00267                     closedir( pCurrDir );
00268                     return true;
00269                 }
00270             }
00271         }
00272 
00273         pDirContent = (dirent*)readdir( pCurrDir );
00274     }
00275 
00276     closedir( pCurrDir );
00277     return false;
00278 }
00279 
00280 
00281 #if !defined( HAVE_LIBTAR_H ) && defined( HAVE_ZLIB_H )
00282 
00283 #ifdef WIN32
00284 #  define mkdir(dirname,mode) _mkdir(dirname)
00285 #endif
00286 
00287 /* Values used in typeflag field */
00288 #define REGTYPE  '0'            /* regular file */
00289 #define AREGTYPE '\0'           /* regular file */
00290 #define DIRTYPE  '5'            /* directory */
00291 
00292 #define BLOCKSIZE 512
00293 
00294 struct tar_header
00295 {                               /* byte offset */
00296     char name[100];             /*   0 */
00297     char mode[8];               /* 100 */
00298     char uid[8];                /* 108 */
00299     char gid[8];                /* 116 */
00300     char size[12];              /* 124 */
00301     char mtime[12];             /* 136 */
00302     char chksum[8];             /* 148 */
00303     char typeflag;              /* 156 */
00304     char linkname[100];         /* 157 */
00305     char magic[6];              /* 257 */
00306     char version[2];            /* 263 */
00307     char uname[32];             /* 265 */
00308     char gname[32];             /* 297 */
00309     char devmajor[8];           /* 329 */
00310     char devminor[8];           /* 337 */
00311     char prefix[155];           /* 345 */
00312                                 /* 500 */
00313 };
00314 
00315 
00316 union tar_buffer {
00317     char              buffer[BLOCKSIZE];
00318     struct tar_header header;
00319 };
00320 
00321 
00322 /* helper functions */
00323 int getoct( char *p, int width );
00324 int makedir( char *newdir );
00325 
00326 int tar_open( TAR **t, char *pathname, int oflags )
00327 {
00328     gzFile f = gzopen( pathname, "rb" );
00329     if( f == NULL )
00330     {
00331         fprintf( stderr, "Couldn't gzopen %s\n", pathname );
00332         return -1;
00333     }
00334 
00335     *t = (gzFile *)malloc( sizeof(gzFile) );
00336     **t = f;
00337     return 0;
00338 }
00339 
00340 
00341 int tar_extract_all( TAR *t, char *prefix )
00342 {
00343     union tar_buffer buffer;
00344     int   len, err, getheader = 1, remaining = 0;
00345     FILE  *outfile = NULL;
00346     char  fname[BLOCKSIZE + PATH_MAX];
00347 
00348     while( 1 )
00349     {
00350         len = gzread( *t, &buffer, BLOCKSIZE );
00351         if( len < 0 )
00352         {
00353             fprintf( stderr, "%s\n", gzerror(*t, &err) );
00354         }
00355 
00356         /*
00357          * Always expect complete blocks to process
00358          * the tar information.
00359          */
00360         if( len != 0 && len != BLOCKSIZE )
00361         {
00362             fprintf( stderr, "gzread: incomplete block read\n" );
00363             return -1;
00364         }
00365 
00366         /*
00367          * If we have to get a tar header
00368          */
00369         if( getheader == 1 )
00370         {
00371             /*
00372              * If we met the end of the tar
00373              * or the end-of-tar block, we are done
00374              */
00375             if( (len == 0) || (buffer.header.name[0] == 0) )
00376             {
00377                 break;
00378             }
00379 
00380             sprintf( fname, "%s/%s", prefix, buffer.header.name );
00381 
00382             /* Check magic value in header */
00383             if( strncmp( buffer.header.magic, "GNUtar", 6 ) &&
00384                 strncmp( buffer.header.magic, "ustar", 5 ) )
00385             {
00386                 //fprintf(stderr, "not a tar file\n");
00387                 return -1;
00388             }
00389 
00390             switch( buffer.header.typeflag )
00391             {
00392                 case DIRTYPE:
00393                     makedir( fname );
00394                     break;
00395                 case REGTYPE:
00396                 case AREGTYPE:
00397                     remaining = getoct( buffer.header.size, 12 );
00398                     if( remaining )
00399                     {
00400                         outfile = fopen( fname, "wb" );
00401                         if( outfile == NULL )
00402                         {
00403                             /* try creating directory */
00404                             char *p = strrchr( fname, '/' );
00405                             if( p != NULL )
00406                             {
00407                                 *p = '\0';
00408                                 makedir( fname );
00409                                 *p = '/';
00410                                 outfile = fopen( fname, "wb" );
00411                                 if( !outfile )
00412                                 {
00413                                     fprintf( stderr, "tar couldn't create %s\n",
00414                                              fname );
00415                                 }
00416                             }
00417                         }
00418                     }
00419                     else outfile = NULL;
00420 
00421                 /*
00422                  * could have no contents
00423                  */
00424                 getheader = (remaining) ? 0 : 1;
00425                 break;
00426             default:
00427                 break;
00428             }
00429         }
00430         else
00431         {
00432             unsigned int bytes = (remaining > BLOCKSIZE)?BLOCKSIZE:remaining;
00433 
00434             if( outfile != NULL )
00435             {
00436                 if( fwrite( &buffer, sizeof(char), bytes, outfile ) != bytes )
00437                 {
00438                     fprintf( stderr, "error writing %s skipping...\n", fname );
00439                     fclose( outfile );
00440                     unlink( fname );
00441                 }
00442             }
00443             remaining -= bytes;
00444             if( remaining == 0 )
00445             {
00446                 getheader = 1;
00447                 if( outfile != NULL )
00448                 {
00449                     fclose(outfile);
00450                     outfile = NULL;
00451                 }
00452             }
00453         }
00454     }
00455 
00456     return 0;
00457 }
00458 
00459 
00460 int tar_close( TAR *t )
00461 {
00462     if( gzclose( *t ) != Z_OK ) fprintf( stderr, "failed gzclose\n" );
00463     free( t );
00464     return 0;
00465 }
00466 
00467 
00468 /* helper functions */
00469 int getoct( char *p, int width )
00470 {
00471     int result = 0;
00472     char c;
00473 
00474     while( width-- )
00475     {
00476         c = *p++;
00477         if( c == ' ' )
00478             continue;
00479         if( c == 0 )
00480             break;
00481         result = result * 8 + (c - '0');
00482     }
00483     return result;
00484 }
00485 
00486 
00487 /* Recursive make directory
00488  * Abort if you get an ENOENT errno somewhere in the middle
00489  * e.g. ignore error "mkdir on existing directory"
00490  *
00491  * return 1 if OK, 0 on error
00492  */
00493 int makedir( char *newdir )
00494 {
00495     char *p, *buffer = strdup( newdir );
00496     int  len = strlen( buffer );
00497 
00498     if( len <= 0 )
00499     {
00500         free( buffer );
00501         return 0;
00502     }
00503 
00504     if( buffer[len-1] == '/' )
00505     {
00506         buffer[len-1] = '\0';
00507     }
00508 
00509     if( mkdir( buffer, 0775 ) == 0 )
00510     {
00511         free( buffer );
00512         return 1;
00513     }
00514 
00515     p = buffer + 1;
00516     while( 1 )
00517     {
00518         char hold;
00519 
00520         while( *p && *p != '\\' && *p != '/' ) p++;
00521         hold = *p;
00522         *p = 0;
00523         if( ( mkdir( buffer, 0775 ) == -1 ) && ( errno == ENOENT ) )
00524         {
00525             fprintf( stderr, "couldn't create directory %s\n", buffer );
00526             free( buffer );
00527             return 0;
00528         }
00529         if( hold == 0 ) break;
00530         *p++ = hold;
00531     }
00532     free( buffer );
00533     return 1;
00534 }
00535 #endif
00536 
00537 #ifdef HAVE_ZLIB_H
00538 
00539 static int currentGzFd = -1;
00540 static void * currentGzVp = NULL;
00541 
00542 int gzopen_frontend( char *pathname, int oflags, int mode )
00543 {
00544     char *gzflags;
00545     gzFile gzf;
00546 
00547     switch( oflags )
00548     {
00549         case O_WRONLY:
00550             gzflags = "wb";
00551             break;
00552         case O_RDONLY:
00553             gzflags = "rb";
00554             break;
00555         case O_RDWR:
00556         default:
00557             errno = EINVAL;
00558             return -1;
00559     }
00560 
00561     gzf = gzopen( pathname, gzflags );
00562     if( !gzf )
00563     {
00564         errno = ENOMEM;
00565         return -1;
00566     }
00567 
00569     currentGzFd = 42;
00570     currentGzVp = gzf;
00571 
00572     return currentGzFd;
00573 }
00574 
00575 int gzclose_frontend( int fd )
00576 {
00577     if( currentGzVp != NULL && fd != -1 )
00578     {
00579         void *toClose = currentGzVp;
00580         currentGzVp = NULL;  currentGzFd = -1;
00581         return gzclose( toClose );
00582     }
00583     return -1;
00584 }
00585 
00586 int gzread_frontend( int fd, void *p_buffer, size_t i_length )
00587 {
00588     if( currentGzVp != NULL && fd != -1 )
00589     {
00590         return gzread( currentGzVp, p_buffer, i_length );
00591     }
00592     return -1;
00593 }
00594 
00595 int gzwrite_frontend( int fd, const void * p_buffer, size_t i_length )
00596 {
00597     if( currentGzVp != NULL && fd != -1 )
00598     {
00599         return gzwrite( currentGzVp, const_cast<void*>(p_buffer), i_length );
00600     }
00601     return -1;
00602 }
00603 
00604 #endif

Generated on Tue Dec 20 10:14:42 2005 for vlc-0.8.4a by  doxygen 1.4.2