2011-12-21 21:43:20 +01:00
/*
* Copyright © 2011 Red Hat , Inc
*
2022-05-18 10:12:45 +02:00
* SPDX - License - Identifier : LGPL - 2.1 - or - later
*
2011-12-21 21:43:20 +01:00
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
2017-05-27 18:21:30 +02:00
* version 2.1 of the License , or ( at your option ) any later version .
2011-12-21 21:43:20 +01:00
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
2014-01-23 12:58:29 +01:00
* License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
2011-12-21 21:43:20 +01:00
*
* Author : Alexander Larsson < alexl @ redhat . com >
*/
# include "config.h"
2012-01-14 22:34:15 +01:00
# include <glib.h>
2011-12-21 21:43:20 +01:00
# include <gstdio.h>
# include <gi18n.h>
# include <string.h>
# include <stdio.h>
# include <locale.h>
2012-01-14 22:34:15 +01:00
# include <errno.h>
Replace #ifdef HAVE_UNISTD_H checks with #ifdef G_OS_UNIX
In Windows development environments that have it, <unistd.h> is mostly
just a wrapper around several other native headers (in particular,
<io.h>, which contains read(), close(), etc, and <process.h>, which
contains getpid()). But given that some Windows dev environments don't
have <unistd.h>, everything that uses those functions on Windows
already needed to include the correct Windows header as well, and so
there is never any point to including <unistd.h> on Windows.
Also, remove some <unistd.h> includes (and a few others) that were
unnecessary even on unix.
https://bugzilla.gnome.org/show_bug.cgi?id=710519
2013-10-19 19:04:00 +02:00
# ifdef G_OS_UNIX
# include <unistd.h>
# endif
2012-02-07 07:41:24 +01:00
# ifdef G_OS_WIN32
# include <io.h>
# endif
2011-12-21 21:43:20 +01:00
2022-10-05 23:45:07 +02:00
# define __GIO_GIO_H_INSIDE__
# include <gio/gioenums.h>
2011-12-21 21:43:20 +01:00
# include <gio/gmemoryoutputstream.h>
# include <gio/gzlibcompressor.h>
# include <gio/gconverteroutputstream.h>
# include <glib.h>
# include "gvdb/gvdb-builder.h"
2012-01-30 16:57:54 +01:00
# include "gconstructor_as_data.h"
2012-11-10 16:51:18 +01:00
# include "glib/glib-private.h"
2011-12-21 21:43:20 +01:00
typedef struct
{
2012-01-23 20:42:20 +01:00
char * filename ;
2011-12-21 21:43:20 +01:00
char * content ;
gsize content_size ;
gsize size ;
guint32 flags ;
} FileData ;
typedef struct
{
GHashTable * table ; /* resource path -> FileData */
2016-08-20 18:16:31 +02:00
gboolean collect_data ;
2011-12-21 21:43:20 +01:00
/* per gresource */
char * prefix ;
/* per file */
char * alias ;
gboolean compressed ;
2012-01-14 22:34:15 +01:00
char * preproc_options ;
2011-12-21 21:43:20 +01:00
GString * string ; /* non-NULL when accepting text */
} ParseState ;
2012-02-09 17:22:36 +01:00
static gchar * * sourcedirs = NULL ;
2012-01-14 22:34:15 +01:00
static gchar * xmllint = NULL ;
2018-03-13 12:49:59 +01:00
static gchar * jsonformat = NULL ;
2012-01-31 16:07:09 +01:00
static gchar * gdk_pixbuf_pixdata = NULL ;
2011-12-21 21:43:20 +01:00
static void
file_data_free ( FileData * data )
{
2012-01-23 20:42:20 +01:00
g_free ( data - > filename ) ;
2011-12-21 21:43:20 +01:00
g_free ( data - > content ) ;
g_free ( data ) ;
}
static void
start_element ( GMarkupParseContext * context ,
const gchar * element_name ,
const gchar * * attribute_names ,
const gchar * * attribute_values ,
gpointer user_data ,
GError * * error )
{
ParseState * state = user_data ;
const GSList * element_stack ;
const gchar * container ;
element_stack = g_markup_parse_context_get_element_stack ( context ) ;
container = element_stack - > next ? element_stack - > next - > data : NULL ;
# define COLLECT(first, ...) \
g_markup_collect_attributes ( element_name , \
attribute_names , attribute_values , error , \
first , __VA_ARGS__ , G_MARKUP_COLLECT_INVALID )
# define OPTIONAL G_MARKUP_COLLECT_OPTIONAL
# define STRDUP G_MARKUP_COLLECT_STRDUP
# define STRING G_MARKUP_COLLECT_STRING
# define BOOL G_MARKUP_COLLECT_BOOLEAN
# define NO_ATTRS() COLLECT (G_MARKUP_COLLECT_INVALID, NULL)
if ( container = = NULL )
{
if ( strcmp ( element_name , " gresources " ) = = 0 )
return ;
}
else if ( strcmp ( container , " gresources " ) = = 0 )
{
if ( strcmp ( element_name , " gresource " ) = = 0 )
{
COLLECT ( OPTIONAL | STRDUP ,
" prefix " , & state - > prefix ) ;
return ;
}
}
else if ( strcmp ( container , " gresource " ) = = 0 )
{
if ( strcmp ( element_name , " file " ) = = 0 )
{
COLLECT ( OPTIONAL | STRDUP , " alias " , & state - > alias ,
2012-01-14 22:34:15 +01:00
OPTIONAL | BOOL , " compressed " , & state - > compressed ,
OPTIONAL | STRDUP , " preprocess " , & state - > preproc_options ) ;
2011-12-21 21:43:20 +01:00
state - > string = g_string_new ( " " ) ;
return ;
}
}
if ( container )
g_set_error ( error , G_MARKUP_ERROR , G_MARKUP_ERROR_UNKNOWN_ELEMENT ,
_ ( " Element <%s> not allowed inside <%s> " ) ,
element_name , container ) ;
else
g_set_error ( error , G_MARKUP_ERROR , G_MARKUP_ERROR_UNKNOWN_ELEMENT ,
_ ( " Element <%s> not allowed at toplevel " ) , element_name ) ;
}
static GvdbItem *
get_parent ( GHashTable * table ,
gchar * key ,
gint length )
{
GvdbItem * grandparent , * parent ;
if ( length = = 1 )
return NULL ;
while ( key [ - - length - 1 ] ! = ' / ' ) ;
key [ length ] = ' \0 ' ;
parent = g_hash_table_lookup ( table , key ) ;
if ( parent = = NULL )
{
parent = gvdb_hash_table_insert ( table , key ) ;
grandparent = get_parent ( table , key , length ) ;
if ( grandparent ! = NULL )
gvdb_item_set_parent ( parent , grandparent ) ;
}
return parent ;
}
2012-02-09 17:22:36 +01:00
static gchar *
find_file ( const gchar * filename )
{
guint i ;
gchar * real_file ;
gboolean exists ;
2012-03-22 14:01:40 +01:00
if ( g_path_is_absolute ( filename ) )
return g_strdup ( filename ) ;
2012-02-09 17:22:36 +01:00
/* search all the sourcedirs for the correct files in order */
for ( i = 0 ; sourcedirs [ i ] ! = NULL ; i + + )
{
2014-04-21 17:00:18 +02:00
real_file = g_build_path ( " / " , sourcedirs [ i ] , filename , NULL ) ;
2012-02-09 17:22:36 +01:00
exists = g_file_test ( real_file , G_FILE_TEST_EXISTS ) ;
if ( exists )
return real_file ;
g_free ( real_file ) ;
}
return NULL ;
}
2011-12-21 21:43:20 +01:00
static void
end_element ( GMarkupParseContext * context ,
const gchar * element_name ,
gpointer user_data ,
GError * * error )
{
ParseState * state = user_data ;
GError * my_error = NULL ;
if ( strcmp ( element_name , " gresource " ) = = 0 )
{
g_free ( state - > prefix ) ;
state - > prefix = NULL ;
}
else if ( strcmp ( element_name , " file " ) = = 0 )
{
2016-06-04 04:20:36 +02:00
gchar * file ;
gchar * real_file = NULL ;
2011-12-21 21:43:20 +01:00
gchar * key ;
2016-03-21 13:44:15 +01:00
FileData * data = NULL ;
2012-01-23 20:42:20 +01:00
char * tmp_file = NULL ;
2011-12-21 21:43:20 +01:00
file = state - > string - > str ;
key = file ;
if ( state - > alias )
key = state - > alias ;
if ( state - > prefix )
key = g_build_path ( " / " , " / " , state - > prefix , key , NULL ) ;
else
key = g_build_path ( " / " , " / " , key , NULL ) ;
if ( g_hash_table_lookup ( state - > table , key ) ! = NULL )
{
g_set_error ( error , G_MARKUP_ERROR , G_MARKUP_ERROR_INVALID_CONTENT ,
_ ( " File %s appears multiple times in the resource " ) ,
key ) ;
return ;
}
2012-02-09 17:22:36 +01:00
if ( sourcedirs ! = NULL )
{
real_file = find_file ( file ) ;
2016-08-20 18:16:31 +02:00
if ( real_file = = NULL & & state - > collect_data )
2012-02-09 17:22:36 +01:00
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
2016-09-30 05:47:15 +02:00
_ ( " Failed to locate “%s” in any source directory " ) , file ) ;
2012-02-09 17:22:36 +01:00
return ;
}
}
2011-12-21 21:43:20 +01:00
else
2012-02-09 17:22:36 +01:00
{
gboolean exists ;
exists = g_file_test ( file , G_FILE_TEST_EXISTS ) ;
2016-08-20 18:16:31 +02:00
if ( ! exists & & state - > collect_data )
2012-02-09 17:22:36 +01:00
{
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
2016-09-30 05:47:15 +02:00
_ ( " Failed to locate “%s” in current directory " ) , file ) ;
2012-02-09 17:22:36 +01:00
return ;
}
}
2011-12-21 21:43:20 +01:00
2016-06-04 04:20:36 +02:00
if ( real_file = = NULL )
real_file = g_strdup ( file ) ;
2016-03-21 13:44:15 +01:00
data = g_new0 ( FileData , 1 ) ;
2012-01-23 20:42:20 +01:00
data - > filename = g_strdup ( real_file ) ;
2016-08-20 18:16:31 +02:00
if ( ! state - > collect_data )
goto done ;
2012-01-23 20:42:20 +01:00
2012-01-14 22:34:15 +01:00
if ( state - > preproc_options )
{
gchar * * options ;
guint i ;
gboolean xml_stripblanks = FALSE ;
2018-03-13 12:49:59 +01:00
gboolean json_stripblanks = FALSE ;
2012-01-31 16:07:09 +01:00
gboolean to_pixdata = FALSE ;
2012-01-14 22:34:15 +01:00
options = g_strsplit ( state - > preproc_options , " , " , - 1 ) ;
for ( i = 0 ; options [ i ] ; i + + )
{
if ( ! strcmp ( options [ i ] , " xml-stripblanks " ) )
xml_stripblanks = TRUE ;
2012-01-31 16:07:09 +01:00
else if ( ! strcmp ( options [ i ] , " to-pixdata " ) )
to_pixdata = TRUE ;
2018-03-13 12:49:59 +01:00
else if ( ! strcmp ( options [ i ] , " json-stripblanks " ) )
json_stripblanks = TRUE ;
2012-01-14 22:34:15 +01:00
else
{
g_set_error ( error , G_MARKUP_ERROR , G_MARKUP_ERROR_INVALID_CONTENT ,
2016-09-30 05:47:15 +02:00
_ ( " Unknown processing option “%s” " ) , options [ i ] ) ;
2012-01-14 22:34:15 +01:00
g_strfreev ( options ) ;
goto cleanup ;
}
}
g_strfreev ( options ) ;
2018-03-14 11:36:01 +01:00
if ( xml_stripblanks )
2012-01-14 22:34:15 +01:00
{
2018-03-14 11:36:01 +01:00
/* This is not fatal: pretty-printed XML is still valid XML */
if ( xmllint = = NULL )
{
static gboolean xmllint_warned = FALSE ;
if ( ! xmllint_warned )
{
/* Translators: the first %s is a gresource XML attribute,
* the second % s is an environment variable , and the third
* % s is a command line tool
*/
char * warn = g_strdup_printf ( _ ( " %s preprocessing requested, but %s is not set, and %s is not in PATH " ) ,
" xml-stripblanks " ,
" XMLLINT " ,
" xmllint " ) ;
g_printerr ( " %s \n " , warn ) ;
g_free ( warn ) ;
/* Only warn once */
xmllint_warned = TRUE ;
}
}
else
{
GSubprocess * proc ;
int fd ;
2012-01-14 22:34:15 +01:00
2018-03-14 11:36:01 +01:00
fd = g_file_open_tmp ( " resource-XXXXXXXX " , & tmp_file , error ) ;
if ( fd < 0 )
goto cleanup ;
2012-01-14 22:34:15 +01:00
2018-03-14 11:36:01 +01:00
close ( fd ) ;
2012-01-14 22:34:15 +01:00
2018-03-14 11:36:01 +01:00
proc = g_subprocess_new ( G_SUBPROCESS_FLAGS_STDOUT_SILENCE , error ,
xmllint , " --nonet " , " --noblanks " , " --output " , tmp_file , real_file , NULL ) ;
g_free ( real_file ) ;
real_file = NULL ;
2012-05-17 20:37:17 +02:00
2018-03-14 11:36:01 +01:00
if ( ! proc )
goto cleanup ;
2012-05-17 20:37:17 +02:00
2018-03-14 11:36:01 +01:00
if ( ! g_subprocess_wait_check ( proc , NULL , error ) )
{
g_object_unref ( proc ) ;
goto cleanup ;
}
2012-01-14 22:34:15 +01:00
2018-03-14 11:36:01 +01:00
g_object_unref ( proc ) ;
2012-05-17 20:37:17 +02:00
2018-03-14 11:36:01 +01:00
real_file = g_strdup ( tmp_file ) ;
}
2012-01-14 22:34:15 +01:00
}
2012-01-31 16:07:09 +01:00
2018-03-14 11:36:01 +01:00
if ( json_stripblanks )
2018-03-13 12:49:59 +01:00
{
2018-03-14 11:36:01 +01:00
/* As above, this is not fatal: pretty-printed JSON is still
* valid JSON
*/
if ( jsonformat = = NULL )
{
static gboolean jsonformat_warned = FALSE ;
if ( ! jsonformat_warned )
{
/* Translators: the first %s is a gresource XML attribute,
* the second % s is an environment variable , and the third
* % s is a command line tool
*/
char * warn = g_strdup_printf ( _ ( " %s preprocessing requested, but %s is not set, and %s is not in PATH " ) ,
" json-stripblanks " ,
" JSON_GLIB_FORMAT " ,
" json-glib-format " ) ;
g_printerr ( " %s \n " , warn ) ;
g_free ( warn ) ;
/* Only warn once */
jsonformat_warned = TRUE ;
}
}
else
{
GSubprocess * proc ;
int fd ;
2018-03-13 12:49:59 +01:00
2018-03-14 11:36:01 +01:00
fd = g_file_open_tmp ( " resource-XXXXXXXX " , & tmp_file , error ) ;
if ( fd < 0 )
goto cleanup ;
2018-03-13 12:49:59 +01:00
2018-03-14 11:36:01 +01:00
close ( fd ) ;
2018-03-13 12:49:59 +01:00
2018-03-14 11:36:01 +01:00
proc = g_subprocess_new ( G_SUBPROCESS_FLAGS_STDOUT_SILENCE , error ,
jsonformat , " --output " , tmp_file , real_file , NULL ) ;
g_free ( real_file ) ;
real_file = NULL ;
2018-03-13 12:49:59 +01:00
2018-03-14 11:36:01 +01:00
if ( ! proc )
goto cleanup ;
2018-03-13 12:49:59 +01:00
2018-03-14 11:36:01 +01:00
if ( ! g_subprocess_wait_check ( proc , NULL , error ) )
{
g_object_unref ( proc ) ;
goto cleanup ;
}
2018-03-13 12:49:59 +01:00
2018-03-14 11:36:01 +01:00
g_object_unref ( proc ) ;
2018-03-13 12:49:59 +01:00
2018-03-14 11:36:01 +01:00
real_file = g_strdup ( tmp_file ) ;
}
2018-03-13 12:49:59 +01:00
}
2012-01-31 18:01:25 +01:00
if ( to_pixdata )
2012-01-31 16:07:09 +01:00
{
2012-05-17 20:37:17 +02:00
GSubprocess * proc ;
2018-03-14 11:30:13 +01:00
int fd ;
2012-01-31 16:07:09 +01:00
2018-03-14 11:36:01 +01:00
/* This is a fatal error: if to-pixdata is used it means that
* the code loading the GResource expects a specific data format
*/
2012-01-31 18:01:25 +01:00
if ( gdk_pixbuf_pixdata = = NULL )
{
2018-03-14 11:36:01 +01:00
/* Translators: the first %s is a gresource XML attribute,
* the second % s is an environment variable , and the third
* % s is a command line tool
*/
g_set_error ( error , G_IO_ERROR , G_IO_ERROR_FAILED ,
_ ( " %s preprocessing requested, but %s is not set, and %s is not in PATH " ) ,
" to-pixdata " ,
" GDK_PIXBUF_PIXDATA " ,
" gdk-pixbuf-pixdata " ) ;
2012-01-31 18:01:25 +01:00
goto cleanup ;
}
2018-03-14 11:30:13 +01:00
fd = g_file_open_tmp ( " resource-XXXXXXXX " , & tmp_file , error ) ;
if ( fd < 0 )
goto cleanup ;
2012-01-31 16:07:09 +01:00
close ( fd ) ;
2012-05-17 20:37:17 +02:00
proc = g_subprocess_new ( G_SUBPROCESS_FLAGS_STDOUT_SILENCE , error ,
2018-03-14 11:30:13 +01:00
gdk_pixbuf_pixdata , real_file , tmp_file , NULL ) ;
2012-05-17 20:37:17 +02:00
g_free ( real_file ) ;
real_file = NULL ;
2012-01-31 16:07:09 +01:00
2012-05-17 20:37:17 +02:00
if ( ! g_subprocess_wait_check ( proc , NULL , error ) )
{
g_object_unref ( proc ) ;
2012-01-31 16:07:09 +01:00
goto cleanup ;
2012-05-17 20:37:17 +02:00
}
g_object_unref ( proc ) ;
2012-01-31 16:07:09 +01:00
2018-03-14 11:30:13 +01:00
real_file = g_strdup ( tmp_file ) ;
2012-01-31 16:07:09 +01:00
}
}
2012-01-14 22:34:15 +01:00
2011-12-21 21:43:20 +01:00
if ( ! g_file_get_contents ( real_file , & data - > content , & data - > size , & my_error ) )
{
g_set_error ( error , G_MARKUP_ERROR , G_MARKUP_ERROR_INVALID_CONTENT ,
_ ( " Error reading file %s: %s " ) ,
real_file , my_error - > message ) ;
g_clear_error ( & my_error ) ;
2012-01-14 22:34:15 +01:00
goto cleanup ;
2011-12-21 21:43:20 +01:00
}
/* Include zero termination in content_size for uncompressed files (but not in size) */
data - > content_size = data - > size + 1 ;
if ( state - > compressed )
{
GOutputStream * out = g_memory_output_stream_new ( NULL , 0 , g_realloc , g_free ) ;
GZlibCompressor * compressor =
g_zlib_compressor_new ( G_ZLIB_COMPRESSOR_FORMAT_ZLIB , 9 ) ;
GOutputStream * out2 = g_converter_output_stream_new ( out , G_CONVERTER ( compressor ) ) ;
if ( ! g_output_stream_write_all ( out2 , data - > content , data - > size ,
NULL , NULL , NULL ) | |
! g_output_stream_close ( out2 , NULL , NULL ) )
{
g_set_error ( error , G_MARKUP_ERROR , G_MARKUP_ERROR_INVALID_CONTENT ,
_ ( " Error compressing file %s " ) ,
real_file ) ;
2019-08-19 22:51:25 +02:00
g_object_unref ( compressor ) ;
g_object_unref ( out ) ;
g_object_unref ( out2 ) ;
goto cleanup ;
2011-12-21 21:43:20 +01:00
}
g_free ( data - > content ) ;
2018-09-18 12:34:23 +02:00
data - > content_size = g_memory_output_stream_get_data_size ( G_MEMORY_OUTPUT_STREAM ( out ) ) ;
2011-12-21 21:43:20 +01:00
data - > content = g_memory_output_stream_steal_data ( G_MEMORY_OUTPUT_STREAM ( out ) ) ;
g_object_unref ( compressor ) ;
g_object_unref ( out ) ;
g_object_unref ( out2 ) ;
data - > flags | = G_RESOURCE_FLAGS_COMPRESSED ;
}
2016-08-20 22:47:34 +02:00
done :
2011-12-21 21:43:20 +01:00
g_hash_table_insert ( state - > table , key , data ) ;
2016-03-21 13:44:15 +01:00
data = NULL ;
2011-12-21 21:43:20 +01:00
2012-01-14 22:34:15 +01:00
cleanup :
2011-12-21 21:43:20 +01:00
/* Cleanup */
g_free ( state - > alias ) ;
state - > alias = NULL ;
g_string_free ( state - > string , TRUE ) ;
state - > string = NULL ;
2012-01-14 22:34:15 +01:00
g_free ( state - > preproc_options ) ;
state - > preproc_options = NULL ;
g_free ( real_file ) ;
2012-01-31 16:07:09 +01:00
2012-01-14 22:34:15 +01:00
if ( tmp_file )
{
unlink ( tmp_file ) ;
g_free ( tmp_file ) ;
}
2012-01-31 16:07:09 +01:00
2016-03-21 13:44:15 +01:00
if ( data ! = NULL )
file_data_free ( data ) ;
2011-12-21 21:43:20 +01:00
}
}
static void
text ( GMarkupParseContext * context ,
const gchar * text ,
gsize text_len ,
gpointer user_data ,
GError * * error )
{
ParseState * state = user_data ;
gsize i ;
for ( i = 0 ; i < text_len ; i + + )
if ( ! g_ascii_isspace ( text [ i ] ) )
{
if ( state - > string )
g_string_append_len ( state - > string , text , text_len ) ;
else
g_set_error ( error , G_MARKUP_ERROR , G_MARKUP_ERROR_INVALID_CONTENT ,
_ ( " text may not appear inside <%s> " ) ,
g_markup_parse_context_get_element ( context ) ) ;
break ;
}
}
static GHashTable *
2012-01-23 20:42:20 +01:00
parse_resource_file ( const gchar * filename ,
2016-08-20 22:47:34 +02:00
gboolean collect_data ,
GHashTable * files )
2011-12-21 21:43:20 +01:00
{
2020-11-17 23:04:06 +01:00
GMarkupParser parser = { start_element , end_element , text , NULL , NULL } ;
2011-12-21 21:43:20 +01:00
ParseState state = { 0 , } ;
GMarkupParseContext * context ;
GError * error = NULL ;
gchar * contents ;
GHashTable * table = NULL ;
gsize size ;
if ( ! g_file_get_contents ( filename , & contents , & size , & error ) )
{
g_printerr ( " %s \n " , error - > message ) ;
g_clear_error ( & error ) ;
return NULL ;
}
2016-08-20 18:16:31 +02:00
state . collect_data = collect_data ;
2016-08-20 22:47:34 +02:00
state . table = g_hash_table_ref ( files ) ;
2011-12-21 21:43:20 +01:00
context = g_markup_parse_context_new ( & parser ,
G_MARKUP_TREAT_CDATA_AS_TEXT |
G_MARKUP_PREFIX_ERROR_POSITION ,
& state , NULL ) ;
if ( ! g_markup_parse_context_parse ( context , contents , size , & error ) | |
! g_markup_parse_context_end_parse ( context , & error ) )
{
g_printerr ( " %s: %s. \n " , filename , error - > message ) ;
g_clear_error ( & error ) ;
}
2016-08-20 22:47:34 +02:00
else
2011-12-21 21:43:20 +01:00
{
GHashTableIter iter ;
const char * key ;
char * mykey ;
gsize key_len ;
FileData * data ;
GVariant * v_data ;
GVariantBuilder builder ;
GvdbItem * item ;
table = gvdb_hash_table_new ( NULL , NULL ) ;
g_hash_table_iter_init ( & iter , state . table ) ;
while ( g_hash_table_iter_next ( & iter , ( gpointer * ) & key , ( gpointer * ) & data ) )
{
key_len = strlen ( key ) ;
mykey = g_strdup ( key ) ;
item = gvdb_hash_table_insert ( table , key ) ;
gvdb_item_set_parent ( item ,
get_parent ( table , mykey , key_len ) ) ;
g_free ( mykey ) ;
g_variant_builder_init ( & builder , G_VARIANT_TYPE ( " (uuay) " ) ) ;
g_variant_builder_add ( & builder , " u " , data - > size ) ; /* Size */
g_variant_builder_add ( & builder , " u " , data - > flags ) ; /* Flags */
v_data = g_variant_new_from_data ( G_VARIANT_TYPE ( " ay " ) ,
data - > content , data - > content_size , TRUE ,
g_free , data - > content ) ;
g_variant_builder_add_value ( & builder , v_data ) ;
data - > content = NULL ; /* Take ownership */
gvdb_item_set_value ( item ,
g_variant_builder_end ( & builder ) ) ;
}
}
2012-01-23 20:42:20 +01:00
g_hash_table_unref ( state . table ) ;
2011-12-21 21:43:20 +01:00
g_markup_parse_context_free ( context ) ;
g_free ( contents ) ;
return table ;
}
static gboolean
write_to_file ( GHashTable * table ,
const gchar * filename ,
GError * * error )
{
gboolean success ;
success = gvdb_table_write_contents ( table , filename ,
G_BYTE_ORDER ! = G_LITTLE_ENDIAN ,
error ) ;
return success ;
}
2015-03-31 20:49:58 +02:00
static gboolean
extension_in_set ( const char * str ,
. . . )
{
va_list list ;
const char * ext , * value ;
gboolean rv = FALSE ;
ext = strrchr ( str , ' . ' ) ;
if ( ext = = NULL )
return FALSE ;
ext + + ;
va_start ( list , str ) ;
while ( ( value = va_arg ( list , const char * ) ) ! = NULL )
{
if ( g_ascii_strcasecmp ( ext , value ) ! = 0 )
continue ;
rv = TRUE ;
break ;
}
va_end ( list ) ;
return rv ;
}
2016-11-15 18:21:46 +01:00
/*
* We must escape any characters that ` make ` finds significant .
* This is largely a duplicate of the logic in gcc ' s ` mkdeps . c : munge ( ) ` .
*/
static char *
escape_makefile_string ( const char * string )
{
GString * str ;
const char * p , * q ;
str = g_string_sized_new ( strlen ( string ) + 1 ) ;
for ( p = string ; * p ! = ' \0 ' ; + + p )
{
switch ( * p )
{
case ' ' :
case ' \t ' :
/* GNU make uses a weird quoting scheme for white space.
A space or tab preceded by 2 N + 1 backslashes represents
N backslashes followed by space ; a space or tab
preceded by 2 N backslashes represents N backslashes at
the end of a file name ; and backslashes in other
contexts should not be doubled . */
for ( q = p - 1 ; string < = q & & * q = = ' \\ ' ; q - - )
g_string_append_c ( str , ' \\ ' ) ;
g_string_append_c ( str , ' \\ ' ) ;
break ;
case ' $ ' :
g_string_append_c ( str , ' $ ' ) ;
break ;
case ' # ' :
g_string_append_c ( str , ' \\ ' ) ;
break ;
}
g_string_append_c ( str , * p ) ;
}
return g_string_free ( str , FALSE ) ;
}
2021-09-18 14:45:57 +02:00
typedef enum {
COMPILER_GCC ,
COMPILER_CLANG ,
COMPILER_MSVC ,
COMPILER_UNKNOWN
} CompilerType ;
/* Get the compiler id from the platform, environment, or command line
*
* Keep compiler IDs consistent with https : //mesonbuild.com/Reference-tables.html#compiler-ids
* for simplicity
*/
static CompilerType
get_compiler_id ( const char * compiler )
{
char * base , * ext_p ;
CompilerType compiler_type ;
if ( compiler = = NULL )
{
# ifdef G_OS_UNIX
const char * compiler_env = g_getenv ( " CC " ) ;
2022-11-02 11:01:09 +01:00
# ifdef __APPLE__
2021-09-18 14:45:57 +02:00
if ( compiler_env = = NULL | | * compiler_env = = ' \0 ' )
compiler = " clang " ;
else
compiler = compiler_env ;
# elif __linux__
if ( compiler_env = = NULL | | * compiler_env = = ' \0 ' )
compiler = " gcc " ;
else
compiler = compiler_env ;
# else
if ( compiler_env = = NULL | | * compiler_env = = ' \0 ' )
compiler = " unknown " ;
else
compiler = compiler_env ;
# endif
# endif
# ifdef G_OS_WIN32
if ( g_getenv ( " MSYSTEM " ) ! = NULL )
{
const char * compiler_env = g_getenv ( " CC " ) ;
if ( compiler_env = = NULL | | * compiler_env = = ' \0 ' )
compiler = " gcc " ;
else
compiler = compiler_env ;
}
else
compiler = " msvc " ;
# endif
}
base = g_path_get_basename ( compiler ) ;
ext_p = strrchr ( base , ' . ' ) ;
if ( ext_p ! = NULL )
{
gsize offset = ext_p - base ;
base [ offset ] = ' \0 ' ;
}
compiler = base ;
if ( g_strcmp0 ( compiler , " gcc " ) = = 0 )
compiler_type = COMPILER_GCC ;
else if ( g_strcmp0 ( compiler , " clang " ) = = 0 )
compiler_type = COMPILER_CLANG ;
else if ( g_strcmp0 ( compiler , " msvc " ) = = 0 )
compiler_type = COMPILER_MSVC ;
else
compiler_type = COMPILER_UNKNOWN ;
g_free ( base ) ;
return compiler_type ;
}
2011-12-21 21:43:20 +01:00
int
main ( int argc , char * * argv )
{
GError * error ;
GHashTable * table ;
2016-08-20 22:47:34 +02:00
GHashTable * files ;
2011-12-21 21:43:20 +01:00
gchar * srcfile ;
2016-09-30 23:56:16 +02:00
gboolean show_version_and_exit = FALSE ;
2011-12-21 21:43:20 +01:00
gchar * target = NULL ;
2012-01-23 20:42:20 +01:00
gchar * binary_target = NULL ;
2012-01-23 22:51:44 +01:00
gboolean generate_automatic = FALSE ;
2011-12-21 21:43:20 +01:00
gboolean generate_source = FALSE ;
gboolean generate_header = FALSE ;
gboolean manual_register = FALSE ;
2012-11-10 04:14:39 +01:00
gboolean internal = FALSE ;
2018-09-09 18:31:38 +02:00
gboolean external_data = FALSE ;
2012-01-23 20:42:20 +01:00
gboolean generate_dependencies = FALSE ;
2016-11-15 14:34:31 +01:00
gboolean generate_phony_targets = FALSE ;
2016-08-20 22:47:34 +02:00
char * dependency_file = NULL ;
2011-12-21 21:43:20 +01:00
char * c_name = NULL ;
2012-01-17 12:32:37 +01:00
char * c_name_no_underscores ;
2012-11-10 04:14:39 +01:00
const char * linkage = " extern " ;
2022-02-17 22:18:27 +01:00
char * compiler = NULL ;
2021-09-18 14:45:57 +02:00
CompilerType compiler_type = COMPILER_GCC ;
2011-12-21 21:43:20 +01:00
GOptionContext * context ;
GOptionEntry entries [ ] = {
2016-09-30 23:56:16 +02:00
{ " version " , 0 , 0 , G_OPTION_ARG_NONE , & show_version_and_exit , N_ ( " Show program version and exit " ) , NULL } ,
2018-06-08 12:43:44 +02:00
{ " target " , 0 , 0 , G_OPTION_ARG_FILENAME , & target , N_ ( " Name of the output file " ) , N_ ( " FILE " ) } ,
2018-06-08 12:42:52 +02:00
{ " sourcedir " , 0 , 0 , G_OPTION_ARG_FILENAME_ARRAY , & sourcedirs , N_ ( " The directories to load files referenced in FILE from (default: current directory) " ) , N_ ( " DIRECTORY " ) } ,
2012-01-23 22:51:44 +01:00
{ " generate " , 0 , 0 , G_OPTION_ARG_NONE , & generate_automatic , N_ ( " Generate output in the format selected for by the target filename extension " ) , NULL } ,
2011-12-21 21:43:20 +01:00
{ " generate-header " , 0 , 0 , G_OPTION_ARG_NONE , & generate_header , N_ ( " Generate source header " ) , NULL } ,
2018-06-08 12:43:44 +02:00
{ " generate-source " , 0 , 0 , G_OPTION_ARG_NONE , & generate_source , N_ ( " Generate source code used to link in the resource file into your code " ) , NULL } ,
2012-01-23 20:42:20 +01:00
{ " generate-dependencies " , 0 , 0 , G_OPTION_ARG_NONE , & generate_dependencies , N_ ( " Generate dependency list " ) , NULL } ,
2018-06-08 12:43:44 +02:00
{ " dependency-file " , 0 , 0 , G_OPTION_ARG_FILENAME , & dependency_file , N_ ( " Name of the dependency file to generate " ) , N_ ( " FILE " ) } ,
2016-11-15 14:34:31 +01:00
{ " generate-phony-targets " , 0 , 0 , G_OPTION_ARG_NONE , & generate_phony_targets , N_ ( " Include phony targets in the generated dependency file " ) , NULL } ,
2016-09-30 05:47:15 +02:00
{ " manual-register " , 0 , 0 , G_OPTION_ARG_NONE , & manual_register , N_ ( " Don’ t automatically create and register resource " ) , NULL } ,
{ " internal " , 0 , 0 , G_OPTION_ARG_NONE , & internal , N_ ( " Don’ t export functions; declare them G_GNUC_INTERNAL " ) , NULL } ,
2018-09-09 18:31:38 +02:00
{ " external-data " , 0 , 0 , G_OPTION_ARG_NONE , & external_data , N_ ( " Don’ t embed resource data in the C file; assume it's linked externally instead " ) , NULL } ,
2011-12-21 21:43:20 +01:00
{ " c-name " , 0 , 0 , G_OPTION_ARG_STRING , & c_name , N_ ( " C identifier name used for the generated source code " ) , NULL } ,
2021-09-18 14:45:57 +02:00
{ " compiler " , ' C ' , 0 , G_OPTION_ARG_STRING , & compiler , N_ ( " The target C compiler (default: the CC environment variable) " ) , NULL } ,
2021-05-13 22:16:46 +02:00
G_OPTION_ENTRY_NULL
2011-12-21 21:43:20 +01:00
} ;
# ifdef G_OS_WIN32
gchar * tmp ;
2023-05-23 12:48:41 +02:00
gchar * * command_line = NULL ;
2011-12-21 21:43:20 +01:00
# endif
2019-06-05 10:42:30 +02:00
setlocale ( LC_ALL , GLIB_DEFAULT_LOCALE ) ;
2011-12-21 21:43:20 +01:00
textdomain ( GETTEXT_PACKAGE ) ;
# ifdef G_OS_WIN32
tmp = _glib_get_locale_dir ( ) ;
bindtextdomain ( GETTEXT_PACKAGE , tmp ) ;
g_free ( tmp ) ;
# else
bindtextdomain ( GETTEXT_PACKAGE , GLIB_LOCALE_DIR ) ;
# endif
# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
bind_textdomain_codeset ( GETTEXT_PACKAGE , " UTF-8 " ) ;
# endif
context = g_option_context_new ( N_ ( " FILE " ) ) ;
g_option_context_set_translation_domain ( context , GETTEXT_PACKAGE ) ;
g_option_context_set_summary ( context ,
N_ ( " Compile a resource specification into a resource file. \n "
" Resource specification files have the extension .gresource.xml, \n "
" and the resource file have the extension called .gresource. " ) ) ;
g_option_context_add_main_entries ( context , entries , GETTEXT_PACKAGE ) ;
error = NULL ;
2023-05-23 12:48:41 +02:00
# ifdef G_OS_WIN32
command_line = g_win32_get_command_line ( ) ;
if ( ! g_option_context_parse_strv ( context , & command_line , & error ) )
{
g_printerr ( " %s \n " , error - > message ) ;
return 1 ;
}
argc = g_strv_length ( command_line ) ;
# else
2011-12-21 21:43:20 +01:00
if ( ! g_option_context_parse ( context , & argc , & argv , & error ) )
{
g_printerr ( " %s \n " , error - > message ) ;
return 1 ;
}
2023-05-23 12:48:41 +02:00
# endif
2011-12-21 21:43:20 +01:00
g_option_context_free ( context ) ;
2016-09-30 23:56:16 +02:00
if ( show_version_and_exit )
{
g_print ( PACKAGE_VERSION " \n " ) ;
return 0 ;
}
2011-12-21 21:43:20 +01:00
if ( argc ! = 2 )
{
g_printerr ( _ ( " You should give exactly one file name \n " ) ) ;
2015-10-29 12:41:48 +01:00
g_free ( c_name ) ;
2011-12-21 21:43:20 +01:00
return 1 ;
}
2012-11-10 04:14:39 +01:00
if ( internal )
linkage = " G_GNUC_INTERNAL " ;
2021-09-18 14:45:57 +02:00
compiler_type = get_compiler_id ( compiler ) ;
2022-02-17 22:18:27 +01:00
g_free ( compiler ) ;
2021-09-18 14:45:57 +02:00
2023-05-23 12:48:41 +02:00
# ifdef G_OS_WIN32
srcfile = command_line [ 1 ] ;
# else
2011-12-21 21:43:20 +01:00
srcfile = argv [ 1 ] ;
2023-05-23 12:48:41 +02:00
# endif
2011-12-21 21:43:20 +01:00
2012-01-14 22:34:15 +01:00
xmllint = g_strdup ( g_getenv ( " XMLLINT " ) ) ;
if ( xmllint = = NULL )
xmllint = g_find_program_in_path ( " xmllint " ) ;
2018-03-13 12:49:59 +01:00
jsonformat = g_strdup ( g_getenv ( " JSON_GLIB_FORMAT " ) ) ;
if ( jsonformat = = NULL )
jsonformat = g_find_program_in_path ( " json-glib-format " ) ;
2012-01-31 16:07:09 +01:00
gdk_pixbuf_pixdata = g_strdup ( g_getenv ( " GDK_PIXBUF_PIXDATA " ) ) ;
if ( gdk_pixbuf_pixdata = = NULL )
gdk_pixbuf_pixdata = g_find_program_in_path ( " gdk-pixbuf-pixdata " ) ;
2011-12-21 21:43:20 +01:00
if ( target = = NULL )
{
char * dirname = g_path_get_dirname ( srcfile ) ;
char * base = g_path_get_basename ( srcfile ) ;
char * target_basename ;
if ( g_str_has_suffix ( base , " .xml " ) )
base [ strlen ( base ) - strlen ( " .xml " ) ] = 0 ;
if ( generate_source )
{
if ( g_str_has_suffix ( base , " .gresource " ) )
base [ strlen ( base ) - strlen ( " .gresource " ) ] = 0 ;
target_basename = g_strconcat ( base , " .c " , NULL ) ;
}
2015-03-25 15:26:07 +01:00
else if ( generate_header )
{
if ( g_str_has_suffix ( base , " .gresource " ) )
base [ strlen ( base ) - strlen ( " .gresource " ) ] = 0 ;
target_basename = g_strconcat ( base , " .h " , NULL ) ;
}
2011-12-21 21:43:20 +01:00
else
{
if ( g_str_has_suffix ( base , " .gresource " ) )
target_basename = g_strdup ( base ) ;
else
target_basename = g_strconcat ( base , " .gresource " , NULL ) ;
}
target = g_build_filename ( dirname , target_basename , NULL ) ;
g_free ( target_basename ) ;
g_free ( dirname ) ;
g_free ( base ) ;
}
2012-01-23 22:51:44 +01:00
else if ( generate_automatic )
{
2015-03-31 20:49:58 +02:00
if ( extension_in_set ( target , " c " , " cc " , " cpp " , " cxx " , " c++ " , NULL ) )
2012-01-23 22:51:44 +01:00
generate_source = TRUE ;
2015-03-31 20:49:58 +02:00
else if ( extension_in_set ( target , " h " , " hh " , " hpp " , " hxx " , " h++ " , NULL ) )
2012-01-23 22:51:44 +01:00
generate_header = TRUE ;
2015-03-31 20:49:58 +02:00
else if ( extension_in_set ( target , " gresource " , NULL ) )
2016-08-25 18:19:37 +02:00
{ }
2012-01-23 22:51:44 +01:00
}
2011-12-21 21:43:20 +01:00
2016-08-20 22:47:34 +02:00
files = g_hash_table_new_full ( g_str_hash , g_str_equal , g_free , ( GDestroyNotify ) file_data_free ) ;
if ( ( table = parse_resource_file ( srcfile , ! generate_dependencies , files ) ) = = NULL )
2011-12-21 21:43:20 +01:00
{
g_free ( target ) ;
2015-10-29 12:41:48 +01:00
g_free ( c_name ) ;
2017-09-25 16:25:17 +02:00
g_hash_table_unref ( files ) ;
2011-12-21 21:43:20 +01:00
return 1 ;
}
2016-10-22 09:54:24 +02:00
/* This can be used in the same invocation
as other generate commands */
if ( dependency_file ! = NULL )
2012-01-23 20:42:20 +01:00
{
2016-10-22 09:54:24 +02:00
/* Generate a .d file that describes the dependencies for
* build tools , gcc - M - MF style */
GString * dep_string ;
2012-01-23 20:42:20 +01:00
GHashTableIter iter ;
gpointer key , data ;
FileData * file_data ;
2016-11-15 18:21:46 +01:00
char * escaped ;
2012-01-23 20:42:20 +01:00
2016-08-20 22:47:34 +02:00
g_hash_table_iter_init ( & iter , files ) ;
2016-10-22 09:54:24 +02:00
dep_string = g_string_new ( NULL ) ;
2023-05-31 21:50:21 +02:00
escaped = escape_makefile_string ( target ) ;
2016-11-15 18:21:46 +01:00
g_string_printf ( dep_string , " %s: " , escaped ) ;
g_free ( escaped ) ;
2016-10-22 09:54:24 +02:00
2023-05-31 21:50:21 +02:00
escaped = escape_makefile_string ( srcfile ) ;
g_string_append_printf ( dep_string , " %s " , escaped ) ;
g_free ( escaped ) ;
/* First rule: foo.c: foo.xml resource1 resource2.. */
2016-10-22 09:54:24 +02:00
while ( g_hash_table_iter_next ( & iter , & key , & data ) )
2016-08-20 22:47:34 +02:00
{
2016-10-22 09:54:24 +02:00
file_data = data ;
if ( ! g_str_equal ( file_data - > filename , srcfile ) )
2016-11-15 18:21:46 +01:00
{
escaped = escape_makefile_string ( file_data - > filename ) ;
g_string_append_printf ( dep_string , " %s " , escaped ) ;
g_free ( escaped ) ;
}
2016-08-20 22:47:34 +02:00
}
2016-11-15 14:34:31 +01:00
g_string_append ( dep_string , " \n " ) ;
2016-08-20 22:47:34 +02:00
2016-11-15 14:34:31 +01:00
/* Optionally include phony targets as it silences `make` but
* isn ' t supported on ` ninja ` at the moment . See also : ` gcc - MP `
*/
if ( generate_phony_targets )
2016-10-22 09:54:24 +02:00
{
2016-11-15 14:34:31 +01:00
g_string_append ( dep_string , " \n " ) ;
/* One rule for every resource: resourceN: */
g_hash_table_iter_init ( & iter , files ) ;
while ( g_hash_table_iter_next ( & iter , & key , & data ) )
{
file_data = data ;
if ( ! g_str_equal ( file_data - > filename , srcfile ) )
2016-11-15 18:21:46 +01:00
{
escaped = escape_makefile_string ( file_data - > filename ) ;
g_string_append_printf ( dep_string , " %s: \n \n " , escaped ) ;
g_free ( escaped ) ;
}
2016-11-15 14:34:31 +01:00
}
2016-10-22 09:54:24 +02:00
}
if ( g_str_equal ( dependency_file , " - " ) )
{
g_print ( " %s \n " , dep_string - > str ) ;
}
else
{
if ( ! g_file_set_contents ( dependency_file , dep_string - > str , dep_string - > len , & error ) )
2016-08-20 22:47:34 +02:00
{
2016-10-22 09:54:24 +02:00
g_printerr ( " Error writing dependency file: %s \n " , error - > message ) ;
g_string_free ( dep_string , TRUE ) ;
g_free ( dependency_file ) ;
g_error_free ( error ) ;
2017-09-25 16:25:17 +02:00
g_hash_table_unref ( files ) ;
2016-10-22 09:54:24 +02:00
return 1 ;
2016-08-20 22:47:34 +02:00
}
2016-10-22 09:54:24 +02:00
}
2016-08-20 22:47:34 +02:00
2016-10-22 09:54:24 +02:00
g_string_free ( dep_string , TRUE ) ;
g_free ( dependency_file ) ;
}
2016-08-20 22:47:34 +02:00
2016-10-22 09:54:24 +02:00
if ( generate_dependencies )
{
GHashTableIter iter ;
gpointer key , data ;
FileData * file_data ;
2016-08-20 22:47:34 +02:00
2016-10-22 09:54:24 +02:00
g_hash_table_iter_init ( & iter , files ) ;
2016-08-20 22:47:34 +02:00
2016-10-22 09:54:24 +02:00
/* Generate list of files for direct use as dependencies in a Makefile */
while ( g_hash_table_iter_next ( & iter , & key , & data ) )
{
file_data = data ;
g_print ( " %s \n " , file_data - > filename ) ;
2012-01-23 20:42:20 +01:00
}
}
else if ( generate_source | | generate_header )
2011-12-21 21:43:20 +01:00
{
if ( generate_source )
{
int fd = g_file_open_tmp ( NULL , & binary_target , NULL ) ;
if ( fd = = - 1 )
{
g_printerr ( " Can't open temp file \n " ) ;
2015-10-29 12:41:48 +01:00
g_free ( c_name ) ;
2017-09-25 16:25:17 +02:00
g_hash_table_unref ( files ) ;
2011-12-21 21:43:20 +01:00
return 1 ;
}
close ( fd ) ;
}
if ( c_name = = NULL )
{
char * base = g_path_get_basename ( srcfile ) ;
GString * s ;
char * dot ;
int i ;
/* Remove extensions */
dot = strchr ( base , ' . ' ) ;
if ( dot )
* dot = 0 ;
s = g_string_new ( " " ) ;
for ( i = 0 ; base [ i ] ! = 0 ; i + + )
{
const char * first = G_CSET_A_2_Z G_CSET_a_2_z " _ " ;
const char * rest = G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS " _ " ;
2019-02-20 11:38:29 +01:00
if ( strchr ( ( s - > len = = 0 ) ? first : rest , base [ i ] ) ! = NULL )
2011-12-21 21:43:20 +01:00
g_string_append_c ( s , base [ i ] ) ;
else if ( base [ i ] = = ' - ' )
g_string_append_c ( s , ' _ ' ) ;
}
2019-02-22 15:47:46 +01:00
g_free ( base ) ;
2011-12-21 21:43:20 +01:00
c_name = g_string_free ( s , FALSE ) ;
}
}
else
binary_target = g_strdup ( target ) ;
2012-01-17 12:32:37 +01:00
c_name_no_underscores = c_name ;
while ( c_name_no_underscores & & * c_name_no_underscores = = ' _ ' )
c_name_no_underscores + + ;
2011-12-21 21:43:20 +01:00
if ( binary_target ! = NULL & &
! write_to_file ( table , binary_target , & error ) )
{
g_printerr ( " %s \n " , error - > message ) ;
g_free ( target ) ;
2015-10-29 12:41:48 +01:00
g_free ( c_name ) ;
2017-09-25 16:25:17 +02:00
g_hash_table_unref ( files ) ;
2011-12-21 21:43:20 +01:00
return 1 ;
}
if ( generate_header )
{
FILE * file ;
file = fopen ( target , " w " ) ;
if ( file = = NULL )
{
g_printerr ( " can't write to file %s " , target ) ;
2015-10-29 12:41:48 +01:00
g_free ( c_name ) ;
2017-09-25 16:25:17 +02:00
g_hash_table_unref ( files ) ;
2011-12-21 21:43:20 +01:00
return 1 ;
}
2018-05-26 09:46:13 +02:00
g_fprintf ( file ,
2011-12-21 21:43:20 +01:00
" #ifndef __RESOURCE_%s_H__ \n "
" #define __RESOURCE_%s_H__ \n "
" \n "
" #include <gio/gio.h> \n "
" \n "
2012-11-10 04:14:39 +01:00
" %s GResource *%s_get_resource (void); \n " ,
c_name , c_name , linkage , c_name ) ;
2011-12-21 21:43:20 +01:00
if ( manual_register )
2018-05-26 09:46:13 +02:00
g_fprintf ( file ,
2011-12-21 21:43:20 +01:00
" \n "
2012-11-10 04:14:39 +01:00
" %s void %s_register_resource (void); \n "
" %s void %s_unregister_resource (void); \n "
2011-12-21 21:43:20 +01:00
" \n " ,
2012-11-10 04:14:39 +01:00
linkage , c_name , linkage , c_name ) ;
2011-12-21 21:43:20 +01:00
2018-05-26 09:46:13 +02:00
g_fprintf ( file ,
2011-12-21 21:43:20 +01:00
" #endif \n " ) ;
fclose ( file ) ;
}
else if ( generate_source )
{
FILE * file ;
guint8 * data ;
gsize data_size ;
gsize i ;
2020-04-29 13:04:36 +02:00
const char * export = " G_MODULE_EXPORT " ;
2011-12-21 21:43:20 +01:00
if ( ! g_file_get_contents ( binary_target , ( char * * ) & data ,
& data_size , NULL ) )
{
g_printerr ( " can't read back temporary file " ) ;
2015-10-29 12:41:48 +01:00
g_free ( c_name ) ;
2017-09-25 16:25:17 +02:00
g_hash_table_unref ( files ) ;
2011-12-21 21:43:20 +01:00
return 1 ;
}
g_unlink ( binary_target ) ;
file = fopen ( target , " w " ) ;
if ( file = = NULL )
{
g_printerr ( " can't write to file %s " , target ) ;
2015-10-29 12:41:48 +01:00
g_free ( c_name ) ;
2017-09-25 16:25:17 +02:00
g_hash_table_unref ( files ) ;
2011-12-21 21:43:20 +01:00
return 1 ;
}
2020-04-29 13:04:36 +02:00
if ( internal )
export = " G_GNUC_INTERNAL " ;
2018-05-26 09:46:13 +02:00
g_fprintf ( file ,
2011-12-21 21:43:20 +01:00
" #include <gio/gio.h> \n "
" \n "
" #if defined (__ELF__) && ( __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6)) \n "
" # define SECTION __attribute__ ((section ( \" .gresource.%s \" ), aligned (8))) \n "
" #else \n "
" # define SECTION \n "
" #endif \n "
2018-11-29 09:12:26 +01:00
" \n " ,
c_name_no_underscores ) ;
2018-09-09 18:31:38 +02:00
if ( external_data )
2018-11-29 09:12:26 +01:00
{
2018-09-09 18:31:38 +02:00
g_fprintf ( file ,
2022-05-12 05:07:09 +02:00
" extern const %s SECTION union { const guint8 data[% " G_GSIZE_FORMAT " ]; const double alignment; void * const ptr;} %s_resource_data; "
2018-09-09 18:31:38 +02:00
" \n " ,
2022-05-12 05:07:09 +02:00
export , data_size , c_name ) ;
2018-11-29 09:12:26 +01:00
}
2018-09-09 18:31:38 +02:00
else
{
2021-09-18 14:45:57 +02:00
if ( compiler_type = = COMPILER_MSVC | | compiler_type = = COMPILER_UNKNOWN )
2018-09-09 18:31:38 +02:00
{
2021-09-18 14:45:57 +02:00
/* For Visual Studio builds: Avoid surpassing the 65535-character limit for a string, GitLab issue #1580 */
g_fprintf ( file ,
" static const SECTION union { const guint8 data[% " G_GSIZE_FORMAT " ]; const double alignment; void * const ptr;} %s_resource_data = { { \n " ,
data_size + 1 /* nul terminator */ , c_name ) ;
2018-11-29 09:12:26 +01:00
2021-09-18 14:45:57 +02:00
for ( i = 0 ; i < data_size ; i + + )
{
if ( i % 16 = = 0 )
g_fprintf ( file , " " ) ;
g_fprintf ( file , " 0%3.3o " , ( int ) data [ i ] ) ;
if ( i ! = data_size - 1 )
g_fprintf ( file , " , " ) ;
if ( i % 16 = = 15 | | i = = data_size - 1 )
g_fprintf ( file , " \n " ) ;
}
2011-12-21 21:43:20 +01:00
2021-09-18 14:45:57 +02:00
g_fprintf ( file , " } }; \n " ) ;
2018-09-09 18:31:38 +02:00
}
2021-09-18 14:45:57 +02:00
else
{
g_fprintf ( file ,
" static const SECTION union { const guint8 data[% " G_GSIZE_FORMAT " ]; const double alignment; void * const ptr;} %s_resource_data = { \n \" " ,
data_size + 1 /* nul terminator */ , c_name ) ;
2011-12-21 21:43:20 +01:00
2021-09-18 14:45:57 +02:00
for ( i = 0 ; i < data_size ; i + + )
{
g_fprintf ( file , " \\ %3.3o " , ( int ) data [ i ] ) ;
if ( i % 16 = = 15 )
g_fprintf ( file , " \" \n \" " ) ;
}
g_fprintf ( file , " \" }; \n " ) ;
}
2018-09-09 18:31:38 +02:00
}
2011-12-21 21:43:20 +01:00
2018-05-26 09:46:13 +02:00
g_fprintf ( file ,
2012-01-31 10:51:44 +01:00
" \n "
2018-09-09 18:31:38 +02:00
" static GStaticResource static_resource = { %s_resource_data.data, sizeof (%s_resource_data.data)%s, NULL, NULL, NULL }; \n "
2020-04-29 13:04:36 +02:00
" \n "
" %s \n "
" GResource *%s_get_resource (void); \n "
2012-01-31 10:51:44 +01:00
" GResource *%s_get_resource (void) \n "
" { \n "
" return g_static_resource_get_resource (&static_resource); \n "
" } \n " ,
2020-04-29 13:04:36 +02:00
c_name , c_name , ( external_data ? " " : " - 1 /* nul terminator */ " ) ,
export , c_name , c_name ) ;
2012-01-31 10:51:44 +01:00
2011-12-21 21:43:20 +01:00
if ( manual_register )
{
2018-05-26 09:46:13 +02:00
g_fprintf ( file ,
2012-01-31 10:51:44 +01:00
" \n "
2020-04-29 13:04:36 +02:00
" %s \n "
" void %s_unregister_resource (void); \n "
2012-01-31 10:51:44 +01:00
" void %s_unregister_resource (void) \n "
" { \n "
" g_static_resource_fini (&static_resource); \n "
" } \n "
" \n "
2020-04-29 13:04:36 +02:00
" %s \n "
" void %s_register_resource (void); \n "
2012-01-31 10:51:44 +01:00
" void %s_register_resource (void) \n "
" { \n "
" g_static_resource_init (&static_resource); \n "
" } \n " ,
2020-04-29 13:04:36 +02:00
export , c_name , c_name ,
export , c_name , c_name ) ;
2011-12-21 21:43:20 +01:00
}
else
{
2018-05-26 09:46:13 +02:00
g_fprintf ( file , " %s " , gconstructor_code ) ;
g_fprintf ( file ,
2011-12-21 21:43:20 +01:00
" \n "
" #ifdef G_HAS_CONSTRUCTORS \n "
" \n "
" #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA \n "
2021-07-26 10:51:26 +02:00
" #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(%sresource_constructor) \n "
2011-12-21 21:43:20 +01:00
" #endif \n "
2021-07-26 10:51:26 +02:00
" G_DEFINE_CONSTRUCTOR(%sresource_constructor) \n "
2011-12-21 21:43:20 +01:00
" #ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA \n "
2021-07-26 10:51:26 +02:00
" #pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(%sresource_destructor) \n "
2011-12-21 21:43:20 +01:00
" #endif \n "
2021-07-26 10:51:26 +02:00
" G_DEFINE_DESTRUCTOR(%sresource_destructor) \n "
2011-12-21 21:43:20 +01:00
" \n "
" #else \n "
" #warning \" Constructor not supported on this compiler, linking in resources will not work \" \n "
" #endif \n "
2012-01-31 10:51:44 +01:00
" \n "
2021-07-26 10:51:26 +02:00
" static void %sresource_constructor (void) \n "
2012-01-31 10:51:44 +01:00
" { \n "
" g_static_resource_init (&static_resource); \n "
" } \n "
" \n "
2021-07-26 10:51:26 +02:00
" static void %sresource_destructor (void) \n "
2012-01-31 10:51:44 +01:00
" { \n "
" g_static_resource_fini (&static_resource); \n "
2021-07-26 10:51:26 +02:00
" } \n " ,
c_name , c_name , c_name ,
c_name , c_name , c_name ) ;
2011-12-21 21:43:20 +01:00
}
fclose ( file ) ;
2012-01-14 21:28:29 +01:00
g_free ( data ) ;
2011-12-21 21:43:20 +01:00
}
g_free ( binary_target ) ;
g_free ( target ) ;
2012-01-14 21:28:29 +01:00
g_hash_table_destroy ( table ) ;
2012-01-14 22:34:15 +01:00
g_free ( xmllint ) ;
2018-03-13 12:49:59 +01:00
g_free ( jsonformat ) ;
2015-10-29 12:41:48 +01:00
g_free ( c_name ) ;
2017-09-25 16:25:17 +02:00
g_hash_table_unref ( files ) ;
2011-12-21 21:43:20 +01:00
2023-05-23 12:48:41 +02:00
# ifdef G_OS_WIN32
g_strfreev ( command_line ) ;
# endif
2011-12-21 21:43:20 +01:00
return 0 ;
}