]> git.sesse.net Git - vlc/blob - modules/gui/skins/src/themeloader.cpp
f431f29578150481d5a1f7e07e6aa9c07ce0244b
[vlc] / modules / gui / skins / src / themeloader.cpp
1 /*****************************************************************************
2  * themeloader.cpp: ThemeLoader class
3  *****************************************************************************
4  * Copyright (C) 2003 VideoLAN
5  * $Id: themeloader.cpp,v 1.9 2003/05/02 15:53:32 gbazin Exp $
6  *
7  * Authors: Olivier Teulière <ipkiss@via.ecp.fr>
8  *          Emmanuel Puig    <karibu@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,
23  * USA.
24  *****************************************************************************/
25
26
27 //--- GENERAL ---------------------------------------------------------------
28 #include <string>
29 #include <fcntl.h>
30 #if !defined( WIN32 )
31 #   include <unistd.h>
32 #else
33 #   include <direct.h>
34 #   ifndef PATH_MAX
35 #       define PATH_MAX MAX_PATH
36 #   endif
37 #endif
38
39 //--- VLC -------------------------------------------------------------------
40 #include <vlc/vlc.h>
41 #include <vlc/intf.h>
42
43 #if defined( HAVE_ZLIB_H )
44 #   include <zlib.h>
45 #   include <errno.h>
46 #endif
47
48 #if defined( HAVE_ZLIB_H ) && defined( HAVE_LIBTAR_H )
49 #   include <libtar.h>
50 #elif defined( HAVE_ZLIB_H )
51     typedef gzFile TAR;
52     int tar_open         ( TAR **t, char *pathname, int oflags );
53     int tar_extract_all  ( TAR *t, char *prefix );
54     int tar_close        ( TAR *t );
55 #endif
56
57 //--- SKIN ------------------------------------------------------------------
58 #include "../os_api.h"
59 #include "theme.h"
60 #include "../os_theme.h"
61 #include "themeloader.h"
62 #include "skin_common.h"
63
64 #include "anchor.h"
65 #include "window.h"
66
67 #define DEFAULT_XML_FILE "theme.xml"
68
69 //---------------------------------------------------------------------------
70 extern "C"
71 {
72     extern FILE *yyin;
73     int yylex();
74 }
75
76 //---------------------------------------------------------------------------
77 ThemeLoader::ThemeLoader( intf_thread_t *_p_intf )
78 {
79     p_intf = _p_intf;
80 }
81 //---------------------------------------------------------------------------
82 ThemeLoader::~ThemeLoader()
83 {
84 }
85 //---------------------------------------------------------------------------
86 #if defined( HAVE_ZLIB_H )
87 int gzopen_frontend( char *pathname, int oflags, int mode )
88 {
89     char *gzflags;
90     gzFile gzf;
91
92     switch( oflags & O_ACCMODE )
93     {
94         case O_WRONLY:
95             gzflags = "wb";
96             break;
97         case O_RDONLY:
98             gzflags = "rb";
99             break;
100         case O_RDWR:
101         default:
102             errno = EINVAL;
103             return -1;
104     }
105
106     gzf = gzopen( pathname, gzflags );
107     if( !gzf )
108     {
109         errno = ENOMEM;
110         return -1;
111     }
112
113     return (int)gzf;
114 }
115 #endif
116
117 //---------------------------------------------------------------------------
118 #if defined( HAVE_ZLIB_H )
119 bool ThemeLoader::ExtractTarGz( const string tarfile, const string rootdir )
120 {
121     TAR *t;
122 #if defined( HAVE_LIBTAR_H )
123     tartype_t gztype = { (openfunc_t) gzopen_frontend, (closefunc_t) gzclose,
124         (readfunc_t) gzread, (writefunc_t) gzwrite };
125
126     if( tar_open( &t, (char *)tarfile.c_str(), &gztype, O_RDONLY, 0,
127                   TAR_GNU ) == -1 )
128 #else
129     if( tar_open( &t, (char *)tarfile.c_str(), O_RDONLY ) == -1 )
130 #endif
131     {
132         return false;
133     }
134
135     if( tar_extract_all( t, (char *)rootdir.c_str() ) != 0 )
136     {
137         return false;
138     }
139
140     if( tar_close( t ) != 0 )
141     {
142         return false;
143     }
144
145     return true;
146 }
147 //---------------------------------------------------------------------------
148 bool ThemeLoader::Extract( const string FileName )
149 {
150     char *tmpdir = tempnam( NULL, "vlt" );
151     string TempPath = tmpdir;
152     free( tmpdir );
153
154     if( ! ExtractTarGz( FileName, TempPath ) )
155         return false;
156
157     if( ! Parse( TempPath + DIRECTORY_SEPARATOR + string( DEFAULT_XML_FILE ) ) )
158     {
159         DeleteTempFiles( TempPath );
160         return false;
161     }
162
163     DeleteTempFiles( TempPath );
164     return true;
165 }
166 //---------------------------------------------------------------------------
167 void ThemeLoader::DeleteTempFiles( const string Path )
168 {
169     OSAPI_RmDir( Path );
170 }
171 #endif
172 //---------------------------------------------------------------------------
173 void ThemeLoader::CleanTheme()
174 {
175     delete (OSTheme *)p_intf->p_sys->p_theme;
176     p_intf->p_sys->p_theme = (Theme *)new OSTheme( p_intf );
177 }
178 //---------------------------------------------------------------------------
179 bool ThemeLoader::Parse( const string XmlFile )
180 {
181     // Things to do before loading theme
182     p_intf->p_sys->p_theme->OnLoadTheme();
183
184     // Set the file to parse
185     yyin = fopen( XmlFile.c_str(), "r" );
186     if( yyin == NULL )
187     {
188         // Skin cannot be opened
189         msg_Warn( p_intf, "Cannot open the specified skin file: %s",
190                   XmlFile.c_str() );
191         return false;
192     }
193
194     // File loaded
195     msg_Dbg( p_intf, "Using skin file: %s", XmlFile.c_str() );
196
197     // Save current working directory
198     char *cwd = new char[PATH_MAX];
199     getcwd( cwd, PATH_MAX );
200
201     // Directory separator is different in each OS !
202     int p = XmlFile.rfind( DIRECTORY_SEPARATOR, XmlFile.size() );
203
204     // Change current working directory to xml file
205     string path = "";
206     if( p > 0 )
207         path = XmlFile.substr( 0, p );
208     chdir( path.c_str() );
209
210     p_intf->p_sys->b_all_win_closed = false;
211
212     // Start the parser
213     int lex = yylex();
214     
215     fclose( yyin );
216     if( lex )
217     {
218         // Set old working directory to current
219         chdir( cwd );
220         delete[] cwd;
221
222         msg_Warn( p_intf, "yylex failed: %i", lex );
223         CleanTheme();
224         return false;
225     }
226
227     // Set old working directory to current
228     chdir( cwd );
229     delete[] cwd;
230
231     return true;
232 }
233 //---------------------------------------------------------------------------
234 bool ThemeLoader::Load( const string FileName )
235 {
236     // First, we try to un-targz the file, and if it fails we hope it's a XML
237     // file...
238 #if defined( HAVE_ZLIB_H )
239     if( ! Extract( FileName ) && ! Parse( FileName ) )
240         return false;
241 #else
242     if( ! Parse( FileName ) )
243         return false;
244 #endif
245
246     // Check if the skin to load is in the config file, to load its config
247     char *skin_last = config_GetPsz( p_intf, "skin_last" );
248     if( skin_last != NULL && FileName == (string)skin_last )
249     {
250         p_intf->p_sys->p_theme->LoadConfig();
251     }
252     else
253     {
254         config_PutPsz( p_intf, "skin_last", FileName.c_str() );
255         config_SaveConfigFile( p_intf, "skins" );
256     }
257
258     return true;
259 }
260 //---------------------------------------------------------------------------
261 #if !defined( HAVE_LIBTAR_H ) && defined( HAVE_ZLIB_H )
262
263 #ifdef WIN32
264 #  define mkdir(dirname,mode) _mkdir(dirname)
265 #endif
266
267 /* Values used in typeflag field.  */
268 #define REGTYPE  '0'            /* regular file */
269 #define AREGTYPE '\0'           /* regular file */
270 #define DIRTYPE  '5'            /* directory */
271
272 #define BLOCKSIZE 512
273
274 struct tar_header
275 {                               /* byte offset */
276   char name[100];               /*   0 */
277   char mode[8];                 /* 100 */
278   char uid[8];                  /* 108 */
279   char gid[8];                  /* 116 */
280   char size[12];                /* 124 */
281   char mtime[12];               /* 136 */
282   char chksum[8];               /* 148 */
283   char typeflag;                /* 156 */
284   char linkname[100];           /* 157 */
285   char magic[6];                /* 257 */
286   char version[2];              /* 263 */
287   char uname[32];               /* 265 */
288   char gname[32];               /* 297 */
289   char devmajor[8];             /* 329 */
290   char devminor[8];             /* 337 */
291   char prefix[155];             /* 345 */
292                                 /* 500 */
293 };
294
295 union tar_buffer {
296   char               buffer[BLOCKSIZE];
297   struct tar_header  header;
298 };
299
300 /* helper functions */
301 int getoct( char *p, int width );
302 int makedir( char *newdir );
303
304 int tar_open( TAR **t, char *pathname, int oflags )
305 {
306     gzFile f = gzopen( pathname, "rb" );
307     if( f == NULL )
308     {
309         fprintf( stderr, "Couldn't gzopen %s\n", pathname );
310         return -1;
311     }
312
313     *t = (gzFile *)malloc( sizeof(gzFile) );
314     **t = f;
315     return 0;
316 }
317
318 int tar_extract_all( TAR *t, char *prefix )
319 {
320     union  tar_buffer buffer;
321     int    len, err, getheader = 1, remaining = 0;
322     FILE   *outfile = NULL;
323     char   fname[BLOCKSIZE + PATH_MAX];
324
325     while( 1 )
326     {
327         len = gzread( *t, &buffer, BLOCKSIZE );
328         if(len < 0) fprintf(stderr, "%s", gzerror(*t, &err) );
329
330         /*
331          * Always expect complete blocks to process
332          * the tar information.
333          */
334         if(len != BLOCKSIZE) fprintf(stderr, "gzread: incomplete block read");
335       
336         /*
337          * If we have to get a tar header
338          */
339         if( getheader == 1 )
340         {
341             /*
342              * if we met the end of the tar
343              * or the end-of-tar block,
344              * we are done
345              */
346             if( (len == 0)  || (buffer.header.name[0]== 0) ) break;
347
348             sprintf( fname, "%s/%s", prefix, buffer.header.name );
349           
350             switch (buffer.header.typeflag)
351             {
352             case DIRTYPE:
353                 makedir( fname );
354                 break;
355             case REGTYPE:
356             case AREGTYPE:
357                 remaining = getoct( buffer.header.size, 12 );
358                 if( remaining )
359                 {
360                     outfile = fopen( fname, "wb" );
361                     if( outfile == NULL )
362                     {
363                         /* try creating directory */
364                         char *p = strrchr( fname, '/' );
365                         if( p != NULL )
366                         {
367                             *p = '\0';
368                             makedir( fname );
369                             *p = '/';
370                             outfile = fopen( fname, "wb" );
371                             if( !outfile )
372                                 fprintf( stderr, "tar couldn't create %s",
373                                          fname );
374                         }
375                     }
376                 }
377                 else outfile = NULL;
378
379                 /*
380                  * could have no contents
381                  */
382                 getheader = (remaining) ? 0 : 1;
383                 break;
384             default:
385                 break;
386             }
387         }
388         else
389         {
390             unsigned int bytes = (remaining > BLOCKSIZE)?BLOCKSIZE:remaining;
391
392             if( outfile != NULL )
393             {
394                 if( fwrite( &buffer, sizeof(char), bytes, outfile ) != bytes )
395                 {
396                     fprintf( stderr, "error writing %s skipping...\n", fname );
397                     fclose( outfile );
398                     unlink( fname );
399                 }
400             }
401             remaining -= bytes;
402             if( remaining == 0 )
403             {
404                 getheader = 1;
405                 if( outfile != NULL )
406                 {
407                     fclose(outfile);
408                     outfile = NULL;
409                 }
410             }
411         }
412     }
413
414     return 0;
415 }
416
417 int tar_close( TAR *t )
418 {
419     if( gzclose( *t ) != Z_OK ) fprintf( stderr, "failed gzclose" );
420     free( t );
421     return 0;
422 }
423
424 /* helper functions */
425 int getoct( char *p, int width )
426 {
427     int result = 0;
428     char c;
429   
430     while( width-- )
431     {
432         c = *p++;
433         if (c == ' ') continue;
434         if (c == 0) break;
435         result = result * 8 + (c - '0');
436     }
437     return result;
438 }
439
440 /* Recursive make directory
441  * Abort if you get an ENOENT errno somewhere in the middle
442  * e.g. ignore error "mkdir on existing directory"
443  *
444  * return 1 if OK, 0 on error
445  */
446 int makedir( char *newdir )
447 {
448     char *p, *buffer = strdup(newdir);
449     int  len = strlen(buffer);
450   
451     if( len <= 0 ) { free(buffer); return 0; }
452
453     if( buffer[len-1] == '/' ) buffer[len-1] = '\0';
454
455     if( mkdir( buffer, 0775 ) == 0 ) { free(buffer); return 1; }
456
457     p = buffer+1;
458     while( 1 )
459     {
460         char hold;
461
462         while( *p && *p != '\\' && *p != '/' ) p++;
463         hold = *p;
464         *p = 0;
465         if( ( mkdir( buffer, 0775 ) == -1 ) && ( errno == ENOENT ) )
466         {
467             fprintf(stderr,"couldn't create directory %s\n", buffer);
468             free(buffer);
469             return 0;
470         }
471         if( hold == 0 ) break;
472         *p++ = hold;
473     }
474     free(buffer);
475     return 1;
476 }
477
478 #endif
479 //---------------------------------------------------------------------------