]> git.sesse.net Git - vlc/blob - modules/gui/skins2/src/theme_loader.cpp
Wrap all gz functions to avoid void* -> int casts.
[vlc] / modules / gui / skins2 / src / theme_loader.cpp
1 /*****************************************************************************
2  * theme_loader.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 #include "theme_loader.hpp"
26 #include "theme.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"
32
33 #ifdef HAVE_FCNTL_H
34 #   include <fcntl.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 <direct.h>
43 #endif
44
45 #ifdef HAVE_DIRENT_H
46 #   include <dirent.h>
47 #endif
48
49
50 #if defined( HAVE_ZLIB_H )
51 #   include <zlib.h>
52 #   include <errno.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 )
58 #   include <libtar.h>
59 #else
60 typedef gzFile TAR;
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 );
64 #endif
65 #endif
66
67 #define DEFAULT_XML_FILE "theme.xml"
68
69
70 bool ThemeLoader::load( const string &fileName )
71 {
72     // First, we try to un-targz the file, and if it fails we hope it's a XML
73     // file...
74 #if defined( HAVE_ZLIB_H )
75     if( ! extract( fileName ) && ! parse( fileName ) )
76         return false;
77 #else
78     if( ! parse( fileName ) )
79         return false;
80 #endif
81
82     Theme *pNewTheme = getIntf()->p_sys->p_theme;
83     if( !pNewTheme )
84     {
85         return false;
86     }
87
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 )
91     {
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();
96     }
97     else
98     {
99         config_PutPsz( getIntf(), "skins2-last", fileName.c_str() );
100         // Show the windows
101         pNewTheme->getWindowManager().showAll();
102     }
103     if( skin_last ) free( skin_last );
104
105     // The new theme cannot embed a video output yet
106     VlcProc::instance( getIntf() )->dropVout();
107
108     return true;
109 }
110
111
112 #if defined( HAVE_ZLIB_H )
113 bool ThemeLoader::extractTarGz( const string &tarFile, const string &rootDir )
114 {
115     TAR *t;
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 };
121
122     if( tar_open( &t, (char *)tarFile.c_str(), &gztype, O_RDONLY, 0,
123                   TAR_GNU ) == -1 )
124 #else
125     if( tar_open( &t, (char *)tarFile.c_str(), O_RDONLY ) == -1 )
126 #endif
127     {
128         return false;
129     }
130
131     if( tar_extract_all( t, (char *)rootDir.c_str() ) != 0 )
132     {
133         tar_close( t );
134         return false;
135     }
136
137     if( tar_close( t ) != 0 )
138     {
139         return false;
140     }
141
142     return true;
143 }
144
145
146 bool ThemeLoader::extract( const string &fileName )
147 {
148     char *tmpdir = tempnam( NULL, "vlt" );
149     string tempPath = tmpdir;
150     free( tmpdir );
151
152     // Extract the file in a temporary directory
153     if( ! extractTarGz( fileName, tempPath ) )
154         return false;
155
156     // Find the XML file and parse it
157     string xmlFile;
158     if( ! findThemeFile( tempPath, xmlFile ) || ! parse( xmlFile ) )
159     {
160         msg_Err( getIntf(), "%s doesn't contain a " DEFAULT_XML_FILE " file",
161                  fileName.c_str() );
162         deleteTempFiles( tempPath );
163         return false;
164     }
165
166     // Clean-up
167     deleteTempFiles( tempPath );
168     return true;
169 }
170
171
172 void ThemeLoader::deleteTempFiles( const string &path )
173 {
174     OSFactory::instance( getIntf() )->rmDir( path );
175 }
176 #endif // HAVE_ZLIB_H
177
178
179 bool ThemeLoader::parse( const string &xmlFile )
180 {
181     // File loaded
182     msg_Dbg( getIntf(), "Using skin file: %s", xmlFile.c_str() );
183
184     // Extract the path of the XML file
185     string path;
186     const string &sep = OSFactory::instance( getIntf() )->getDirSeparator();
187     string::size_type p = xmlFile.rfind( sep, xmlFile.size() );
188     if( p != string::npos )
189     {
190         path = xmlFile.substr( 0, p + 1 );
191     }
192     else
193     {
194         path = "";
195     }
196
197     // Start the parser
198     SkinParser parser( getIntf(), xmlFile, path );
199     if( ! parser.parse() )
200     {
201         msg_Err( getIntf(), "Failed to parse %s", xmlFile.c_str() );
202         return false;
203     }
204
205     // Build and store the theme
206     Builder builder( getIntf(), parser.getData() );
207     getIntf()->p_sys->p_theme = builder.build();
208
209     return true;
210 }
211
212
213 bool ThemeLoader::findThemeFile( const string &rootDir, string &themeFilePath )
214 {
215     // Path separator
216     const string &sep = OSFactory::instance( getIntf() )->getDirSeparator();
217
218     DIR *pCurrDir;
219     struct dirent *pDirContent;
220
221     // Open the dir
222     pCurrDir = opendir( rootDir.c_str() );
223
224     if( pCurrDir == NULL )
225     {
226         // An error occurred
227         msg_Dbg( getIntf(), "Cannot open directory %s", rootDir.c_str() );
228         return false;
229     }
230
231     // Get the first directory entry
232     pDirContent = (dirent*)readdir( pCurrDir );
233
234     // While we still have entries in the directory
235     while( pDirContent != NULL )
236     {
237         string newURI = rootDir + sep + pDirContent->d_name;
238
239         // Skip . and ..
240         if( string( pDirContent->d_name ) != "." &&
241             string( pDirContent->d_name ) != ".." )
242         {
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 )
249 #else
250             if( 0 )
251 #endif
252             {
253                 // Can we find the theme file in this subdirectory?
254                 if( findThemeFile( newURI, themeFilePath ) )
255                 {
256                     closedir( pCurrDir );
257                     return true;
258                 }
259             }
260             else
261             {
262                 // Found the theme file?
263                 if( string( DEFAULT_XML_FILE ) ==
264                     string( pDirContent->d_name ) )
265                 {
266                     themeFilePath = newURI;
267                     closedir( pCurrDir );
268                     return true;
269                 }
270             }
271         }
272
273         pDirContent = (dirent*)readdir( pCurrDir );
274     }
275
276     closedir( pCurrDir );
277     return false;
278 }
279
280
281 #if !defined( HAVE_LIBTAR_H ) && defined( HAVE_ZLIB_H )
282
283 #ifdef WIN32
284 #  define mkdir(dirname,mode) _mkdir(dirname)
285 #endif
286
287 /* Values used in typeflag field */
288 #define REGTYPE  '0'            /* regular file */
289 #define AREGTYPE '\0'           /* regular file */
290 #define DIRTYPE  '5'            /* directory */
291
292 #define BLOCKSIZE 512
293
294 struct tar_header
295 {                               /* byte offset */
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 */
312                                 /* 500 */
313 };
314
315
316 union tar_buffer {
317     char              buffer[BLOCKSIZE];
318     struct tar_header header;
319 };
320
321
322 /* helper functions */
323 int getoct( char *p, int width );
324 int makedir( char *newdir );
325
326 int tar_open( TAR **t, char *pathname, int oflags )
327 {
328     gzFile f = gzopen( pathname, "rb" );
329     if( f == NULL )
330     {
331         fprintf( stderr, "Couldn't gzopen %s\n", pathname );
332         return -1;
333     }
334
335     *t = (gzFile *)malloc( sizeof(gzFile) );
336     **t = f;
337     return 0;
338 }
339
340
341 int tar_extract_all( TAR *t, char *prefix )
342 {
343     union tar_buffer buffer;
344     int   len, err, getheader = 1, remaining = 0;
345     FILE  *outfile = NULL;
346     char  fname[BLOCKSIZE + PATH_MAX];
347
348     while( 1 )
349     {
350         len = gzread( *t, &buffer, BLOCKSIZE );
351         if( len < 0 )
352         {
353             fprintf( stderr, "%s\n", gzerror(*t, &err) );
354         }
355
356         /*
357          * Always expect complete blocks to process
358          * the tar information.
359          */
360         if( len != 0 && len != BLOCKSIZE )
361         {
362             fprintf( stderr, "gzread: incomplete block read\n" );
363             return -1;
364         }
365
366         /*
367          * If we have to get a tar header
368          */
369         if( getheader == 1 )
370         {
371             /*
372              * If we met the end of the tar
373              * or the end-of-tar block, we are done
374              */
375             if( (len == 0) || (buffer.header.name[0] == 0) )
376             {
377                 break;
378             }
379
380             sprintf( fname, "%s/%s", prefix, buffer.header.name );
381
382             /* Check magic value in header */
383             if( strncmp( buffer.header.magic, "GNUtar", 6 ) &&
384                 strncmp( buffer.header.magic, "ustar", 5 ) )
385             {
386                 //fprintf(stderr, "not a tar file\n");
387                 return -1;
388             }
389
390             switch( buffer.header.typeflag )
391             {
392                 case DIRTYPE:
393                     makedir( fname );
394                     break;
395                 case REGTYPE:
396                 case AREGTYPE:
397                     remaining = getoct( buffer.header.size, 12 );
398                     if( remaining )
399                     {
400                         outfile = fopen( fname, "wb" );
401                         if( outfile == NULL )
402                         {
403                             /* try creating directory */
404                             char *p = strrchr( fname, '/' );
405                             if( p != NULL )
406                             {
407                                 *p = '\0';
408                                 makedir( fname );
409                                 *p = '/';
410                                 outfile = fopen( fname, "wb" );
411                                 if( !outfile )
412                                 {
413                                     fprintf( stderr, "tar couldn't create %s\n",
414                                              fname );
415                                 }
416                             }
417                         }
418                     }
419                     else outfile = NULL;
420
421                 /*
422                  * could have no contents
423                  */
424                 getheader = (remaining) ? 0 : 1;
425                 break;
426             default:
427                 break;
428             }
429         }
430         else
431         {
432             unsigned int bytes = (remaining > BLOCKSIZE)?BLOCKSIZE:remaining;
433
434             if( outfile != NULL )
435             {
436                 if( fwrite( &buffer, sizeof(char), bytes, outfile ) != bytes )
437                 {
438                     fprintf( stderr, "error writing %s skipping...\n", fname );
439                     fclose( outfile );
440                     unlink( fname );
441                 }
442             }
443             remaining -= bytes;
444             if( remaining == 0 )
445             {
446                 getheader = 1;
447                 if( outfile != NULL )
448                 {
449                     fclose(outfile);
450                     outfile = NULL;
451                 }
452             }
453         }
454     }
455
456     return 0;
457 }
458
459
460 int tar_close( TAR *t )
461 {
462     if( gzclose( *t ) != Z_OK ) fprintf( stderr, "failed gzclose\n" );
463     free( t );
464     return 0;
465 }
466
467
468 /* helper functions */
469 int getoct( char *p, int width )
470 {
471     int result = 0;
472     char c;
473
474     while( width-- )
475     {
476         c = *p++;
477         if( c == ' ' )
478             continue;
479         if( c == 0 )
480             break;
481         result = result * 8 + (c - '0');
482     }
483     return result;
484 }
485
486
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"
490  *
491  * return 1 if OK, 0 on error
492  */
493 int makedir( char *newdir )
494 {
495     char *p, *buffer = strdup( newdir );
496     int  len = strlen( buffer );
497
498     if( len <= 0 )
499     {
500         free( buffer );
501         return 0;
502     }
503
504     if( buffer[len-1] == '/' )
505     {
506         buffer[len-1] = '\0';
507     }
508
509     if( mkdir( buffer, 0775 ) == 0 )
510     {
511         free( buffer );
512         return 1;
513     }
514
515     p = buffer + 1;
516     while( 1 )
517     {
518         char hold;
519
520         while( *p && *p != '\\' && *p != '/' ) p++;
521         hold = *p;
522         *p = 0;
523         if( ( mkdir( buffer, 0775 ) == -1 ) && ( errno == ENOENT ) )
524         {
525             fprintf( stderr, "couldn't create directory %s\n", buffer );
526             free( buffer );
527             return 0;
528         }
529         if( hold == 0 ) break;
530         *p++ = hold;
531     }
532     free( buffer );
533     return 1;
534 }
535 #endif
536
537 #ifdef HAVE_ZLIB_H
538
539 static int currentGzFd = -1;
540 static void * currentGzVp = NULL;
541
542 int gzopen_frontend( char *pathname, int oflags, int mode )
543 {
544     char *gzflags;
545     gzFile gzf;
546
547     switch( oflags )
548     {
549         case O_WRONLY:
550             gzflags = "wb";
551             break;
552         case O_RDONLY:
553             gzflags = "rb";
554             break;
555         case O_RDWR:
556         default:
557             errno = EINVAL;
558             return -1;
559     }
560
561     gzf = gzopen( pathname, gzflags );
562     if( !gzf )
563     {
564         errno = ENOMEM;
565         return -1;
566     }
567
568     /** Hum ... */
569     currentGzFd = 42;
570     currentGzVp = gzf;
571
572     return currentGzFd;
573 }
574
575 int gzclose_frontend( int fd )
576 {
577     if( currentGzVp != NULL && fd != -1 )
578     {
579         return gzclose( currentGzVp );
580     }
581     return -1;
582 }
583
584 int gzread_frontend( int fd, void *p_buffer, size_t i_length )
585 {
586     if( currentGzVp != NULL && fd != -1 )
587     {
588         return gzread( currentGzVp, p_buffer, i_length );
589     }
590     return -1;
591 }
592
593 int gzwrite_frontend( int fd, const void * p_buffer, size_t i_length )
594 {
595     if( currentGzVp != NULL && fd != -1 )
596     {
597         return gzwrite( currentGzVp, p_buffer, i_length );
598     }
599     return -1;
600 }
601
602 #endif