]> git.sesse.net Git - vlc/blob - modules/gui/skins/src/themeloader.cpp
* repaired basic_skins
[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.14 2003/06/09 12:33:16 asmax 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     {   tar_close( t );
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         msg_Err( p_intf, "%s doesn't contain a " DEFAULT_XML_FILE " file",
160                  FileName.c_str() );
161         DeleteTempFiles( TempPath );
162         return false;
163     }
164
165     DeleteTempFiles( TempPath );
166     return true;
167 }
168 //---------------------------------------------------------------------------
169 void ThemeLoader::DeleteTempFiles( const string Path )
170 {
171     OSAPI_RmDir( Path );
172 }
173 #endif
174 //---------------------------------------------------------------------------
175 void ThemeLoader::CleanTheme()
176 {
177     delete (OSTheme *)p_intf->p_sys->p_theme;
178     p_intf->p_sys->p_theme = (Theme *)new OSTheme( p_intf );
179 }
180 //---------------------------------------------------------------------------
181 bool ThemeLoader::Parse( const string XmlFile )
182 {
183     // Things to do before loading theme
184     p_intf->p_sys->p_theme->OnLoadTheme();
185
186     // Set the file to parse
187     yyin = fopen( XmlFile.c_str(), "r" );
188     if( yyin == NULL )
189     {
190         // Skin cannot be opened
191         msg_Warn( p_intf, "Cannot open the specified skin file: %s",
192                   XmlFile.c_str() );
193         return false;
194     }
195
196     // File loaded
197     msg_Dbg( p_intf, "Using skin file: %s", XmlFile.c_str() );
198
199     // Save current working directory
200     char *cwd = new char[PATH_MAX];
201     getcwd( cwd, PATH_MAX );
202
203     // Directory separator is different in each OS !
204     int p = XmlFile.rfind( DIRECTORY_SEPARATOR, XmlFile.size() );
205
206     // Change current working directory to xml file
207     string path = "";
208     if( p > 0 )
209         path = XmlFile.substr( 0, p );
210     chdir( path.c_str() );
211
212     p_intf->p_sys->b_all_win_closed = false;
213
214     // Start the parser
215     int lex = yylex();
216     
217     fclose( yyin );
218     if( lex )
219     {
220         // Set old working directory to current
221         chdir( cwd );
222         delete[] cwd;
223
224         msg_Warn( p_intf, "yylex failed: %i", lex );
225         CleanTheme();
226         return false;
227     }
228
229     // Set old working directory to current
230     chdir( cwd );
231     delete[] cwd;
232
233     return true;
234 }
235 //---------------------------------------------------------------------------
236 bool ThemeLoader::Load( const string FileName )
237 {
238     // First, we try to un-targz the file, and if it fails we hope it's a XML
239     // file...
240 #if defined( HAVE_ZLIB_H )
241     if( ! Extract( FileName ) && ! Parse( FileName ) )
242         return false;
243 #else
244     if( ! Parse( FileName ) )
245         return false;
246 #endif
247
248     // Check if the skin to load is in the config file, to load its config
249     char *skin_last = config_GetPsz( p_intf, "skin_last" );
250     if( skin_last != NULL && FileName == (string)skin_last )
251     {
252         p_intf->p_sys->p_theme->LoadConfig();
253     }
254     else
255     {
256         config_PutPsz( p_intf, "skin_last", FileName.c_str() );
257         config_SaveConfigFile( p_intf, "skins" );
258     }
259
260     return true;
261 }
262 //---------------------------------------------------------------------------
263 #if !defined( HAVE_LIBTAR_H ) && defined( HAVE_ZLIB_H )
264
265 #ifdef WIN32
266 #  define mkdir(dirname,mode) _mkdir(dirname)
267 #endif
268
269 /* Values used in typeflag field.  */
270 #define REGTYPE  '0'            /* regular file */
271 #define AREGTYPE '\0'           /* regular file */
272 #define DIRTYPE  '5'            /* directory */
273
274 #define BLOCKSIZE 512
275
276 struct tar_header
277 {                               /* byte offset */
278   char name[100];               /*   0 */
279   char mode[8];                 /* 100 */
280   char uid[8];                  /* 108 */
281   char gid[8];                  /* 116 */
282   char size[12];                /* 124 */
283   char mtime[12];               /* 136 */
284   char chksum[8];               /* 148 */
285   char typeflag;                /* 156 */
286   char linkname[100];           /* 157 */
287   char magic[6];                /* 257 */
288   char version[2];              /* 263 */
289   char uname[32];               /* 265 */
290   char gname[32];               /* 297 */
291   char devmajor[8];             /* 329 */
292   char devminor[8];             /* 337 */
293   char prefix[155];             /* 345 */
294                                 /* 500 */
295 };
296
297 union tar_buffer {
298   char               buffer[BLOCKSIZE];
299   struct tar_header  header;
300 };
301
302 /* helper functions */
303 int getoct( char *p, int width );
304 int makedir( char *newdir );
305
306 int tar_open( TAR **t, char *pathname, int oflags )
307 {
308     gzFile f = gzopen( pathname, "rb" );
309     if( f == NULL )
310     {
311         fprintf( stderr, "Couldn't gzopen %s\n", pathname );
312         return -1;
313     }
314
315     *t = (gzFile *)malloc( sizeof(gzFile) );
316     **t = f;
317     return 0;
318 }
319
320 int tar_extract_all( TAR *t, char *prefix )
321 {
322     union  tar_buffer buffer;
323     int    len, err, getheader = 1, remaining = 0;
324     FILE   *outfile = NULL;
325     char   fname[BLOCKSIZE + PATH_MAX];
326
327     while( 1 )
328     {
329         len = gzread( *t, &buffer, BLOCKSIZE );
330         if(len < 0) fprintf(stderr, "%s", gzerror(*t, &err) );
331
332         /*
333          * Always expect complete blocks to process
334          * the tar information.
335          */
336         if( len != 0 && len != BLOCKSIZE )
337         {
338             fprintf(stderr, "gzread: incomplete block read");
339             return -1;
340         }
341
342         /*
343          * If we have to get a tar header
344          */
345         if( getheader == 1 )
346         {
347             /*
348              * if we met the end of the tar
349              * or the end-of-tar block,
350              * we are done
351              */
352             if( (len == 0)  || (buffer.header.name[0]== 0) ) break;
353
354             sprintf( fname, "%s/%s", prefix, buffer.header.name );
355           
356             switch (buffer.header.typeflag)
357             {
358             case DIRTYPE:
359                 makedir( fname );
360                 break;
361             case REGTYPE:
362             case AREGTYPE:
363                 remaining = getoct( buffer.header.size, 12 );
364                 if( remaining )
365                 {
366                     outfile = fopen( fname, "wb" );
367                     if( outfile == NULL )
368                     {
369                         /* try creating directory */
370                         char *p = strrchr( fname, '/' );
371                         if( p != NULL )
372                         {
373                             *p = '\0';
374                             makedir( fname );
375                             *p = '/';
376                             outfile = fopen( fname, "wb" );
377                             if( !outfile )
378                                 fprintf( stderr, "tar couldn't create %s",
379                                          fname );
380                         }
381                     }
382                 }
383                 else outfile = NULL;
384
385                 /*
386                  * could have no contents
387                  */
388                 getheader = (remaining) ? 0 : 1;
389                 break;
390             default:
391                 break;
392             }
393         }
394         else
395         {
396             unsigned int bytes = (remaining > BLOCKSIZE)?BLOCKSIZE:remaining;
397
398             if( outfile != NULL )
399             {
400                 if( fwrite( &buffer, sizeof(char), bytes, outfile ) != bytes )
401                 {
402                     fprintf( stderr, "error writing %s skipping...\n", fname );
403                     fclose( outfile );
404                     unlink( fname );
405                 }
406             }
407             remaining -= bytes;
408             if( remaining == 0 )
409             {
410                 getheader = 1;
411                 if( outfile != NULL )
412                 {
413                     fclose(outfile);
414                     outfile = NULL;
415                 }
416             }
417         }
418     }
419
420     return 0;
421 }
422
423 int tar_close( TAR *t )
424 {
425     if( gzclose( *t ) != Z_OK ) fprintf( stderr, "failed gzclose" );
426     free( t );
427     return 0;
428 }
429
430 /* helper functions */
431 int getoct( char *p, int width )
432 {
433     int result = 0;
434     char c;
435   
436     while( width-- )
437     {
438         c = *p++;
439         if (c == ' ') continue;
440         if (c == 0) break;
441         result = result * 8 + (c - '0');
442     }
443     return result;
444 }
445
446 /* Recursive make directory
447  * Abort if you get an ENOENT errno somewhere in the middle
448  * e.g. ignore error "mkdir on existing directory"
449  *
450  * return 1 if OK, 0 on error
451  */
452 int makedir( char *newdir )
453 {
454     char *p, *buffer = strdup(newdir);
455     int  len = strlen(buffer);
456   
457     if( len <= 0 ) { free(buffer); return 0; }
458
459     if( buffer[len-1] == '/' ) buffer[len-1] = '\0';
460
461     if( mkdir( buffer, 0775 ) == 0 ) { free(buffer); return 1; }
462
463     p = buffer+1;
464     while( 1 )
465     {
466         char hold;
467
468         while( *p && *p != '\\' && *p != '/' ) p++;
469         hold = *p;
470         *p = 0;
471         if( ( mkdir( buffer, 0775 ) == -1 ) && ( errno == ENOENT ) )
472         {
473             fprintf(stderr,"couldn't create directory %s\n", buffer);
474             free(buffer);
475             return 0;
476         }
477         if( hold == 0 ) break;
478         *p++ = hold;
479     }
480     free(buffer);
481     return 1;
482 }
483
484 #endif
485 //---------------------------------------------------------------------------