/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * gsf-utils.c: * * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2.1 of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Glib gets this wrong, really. ARM's floating point format is a weird * mixture. */ #define G_ARMFLOAT_ENDIAN 56781234 #if defined(__arm__) && !defined(__vfp__) && (G_BYTE_ORDER == G_LITTLE_ENDIAN) #define G_FLOAT_BYTE_ORDER G_ARMFLOAT_ENDIAN #else #define G_FLOAT_BYTE_ORDER G_BYTE_ORDER #endif static void base64_init (void); #ifdef G_OS_WIN32 #include G_WIN32_DLLMAIN_FOR_DLL_NAME (static, dll_name) #endif #ifdef _GSF_GTYPE_THREADING_FIXED typedef GTypeModule GsfDummyTypeModule; typedef GTypeModuleClass GsfDummyTypeModuleClass; static gboolean gsf_dummy_type_module_load (GTypeModule *module) { gsf_init_dynamic (module); return TRUE; } static void gsf_dummy_type_module_class_init (GTypeModuleClass *gtm_class) { gtm_class->load = gsf_dummy_type_module_load; } static GSF_CLASS (GsfDummyTypeModule, gsf_dummy_type_module, gsf_dummy_type_module_class_init, NULL, G_TYPE_TYPE_MODULE) static GTypeModule *static_type_module = NULL; #endif /** * gsf_init : * * Initializes the GSF library **/ void gsf_init (void) { #ifdef ENABLE_NLS #ifdef G_OS_WIN32 #undef GNOMELOCALEDIR gchar *prefix = g_win32_get_package_installation_directory (NULL, dll_name); gchar *GNOMELOCALEDIR = g_build_filename (prefix, "lib/locale", NULL); g_free (prefix); #endif bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR); #ifdef G_OS_WIN32 g_free (GNOMELOCALEDIR); #endif bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); #endif g_type_init (); base64_init (); #ifdef _GSF_GTYPE_THREADING_FIXED if (NULL == static_type_module) { static_type_module = g_object_new (gsf_dummy_type_module_get_type(), NULL); g_assert (static_type_module != NULL); g_type_module_use (static_type_module); g_type_module_set_name (static_type_module, "libgsf-builtin"); } #else gsf_init_dynamic (NULL); #endif } /** * gsf_shutdown: * * De-intializes the GSF library * Currently does nothing. **/ void gsf_shutdown (void) { } #ifdef _GSF_GTYPE_THREADING_FIXED #define REGISTER(prefix) \ do { \ prefix ## _register_type (module); \ types = g_slist_prepend (types, \ g_type_class_ref (prefix ## _get_type())); \ } while (0) #else /* Assign the value to avoid compiler warnings */ #define REGISTER(prefix) t = prefix ## _get_type() #endif /** * gsf_init_dynamic : * @module : #GTypeModule. * * Initializes the GSF library and associates it with a type module @mod. **/ void gsf_init_dynamic (GTypeModule *module) { #ifndef _GSF_GTYPE_THREADING_FIXED GType t; if (NULL != module) { g_warning ("glib's support of dynamic types is not thread safe.\n" "Support for gsf_init_dynamic has been disabled until that is fixed"); } #endif REGISTER (gsf_input); REGISTER (gsf_input_gzip); REGISTER (gsf_input_http); REGISTER (gsf_input_memory); REGISTER (gsf_input_proxy); REGISTER (gsf_input_stdio); REGISTER (gsf_input_textline); REGISTER (gsf_infile); REGISTER (gsf_infile_msole); REGISTER (gsf_infile_msvba); REGISTER (gsf_infile_stdio); REGISTER (gsf_infile_zip); REGISTER (gsf_output); REGISTER (gsf_output_bzip); REGISTER (gsf_output_csv_quoting_mode); REGISTER (gsf_output_csv); REGISTER (gsf_output_gzip); REGISTER (gsf_output_iconv); REGISTER (gsf_output_iochannel); REGISTER (gsf_output_memory); REGISTER (gsf_output_stdio); REGISTER (gsf_outfile); REGISTER (gsf_outfile_msole); REGISTER (gsf_outfile_stdio); REGISTER (gsf_outfile_zip); REGISTER (gsf_outfile_open_pkg); REGISTER (gsf_shared_memory); REGISTER (gsf_structured_blob); REGISTER (gsf_xml_out); REGISTER (gsf_blob); REGISTER (gsf_clip_data); REGISTER (gsf_doc_meta_data); REGISTER (gsf_docprop_vector); } /** * gsf_shutdown: * * De-intializes the GSF library from a type module. * Currently does nothing. **/ void gsf_shutdown_dynamic (G_GNUC_UNUSED GTypeModule *module) { } static void gsf_mem_dump_full (guint8 const *ptr, size_t len, gsf_off_t offset) { static const char hexdigit[16] = "0123456789abcdef"; while (len > 0) { char hexpart[3 * 16 + 1], *phex = hexpart; char pic[17]; size_t j; for (j = 0; j < 16; j++) { if (len > 0) { *phex++ = hexdigit[*ptr >> 4]; *phex++ = hexdigit[*ptr & 0xf]; pic[j] = (*ptr >= '!' && *ptr < 127 ? *ptr : '.'); len--; ptr++; } else { *phex++ = 'X'; *phex++ = 'X'; pic[j] = '*'; } *phex++ = ' '; } hexpart[3 * 16] = 0; pic[16] = 0 ; g_print ("%8lx | %s| %s\n", (long)offset, hexpart, pic); offset += 16; } } /** * gsf_mem_dump : * @ptr: memory area to be dumped. * @len: how many bytes will be dumped. * * Dump @len bytes from the memory location given by @ptr. **/ void gsf_mem_dump (guint8 const *ptr, size_t len) { gsf_mem_dump_full (ptr, len, 0); } /** * gsf_input_dump : * @input: a #GsfInput * @dump_as_hex: If %TRUE, dump in hexidecmal format * * Dumps @input's contents to STDOUT, optionally in hex format. */ void gsf_input_dump (GsfInput *input, gboolean dump_as_hex) { gsf_off_t offset = 0; size_t size, count; guint8 const *data; /* read in small blocks to excercise things */ size = gsf_input_size (GSF_INPUT (input)); while (size > 0) { count = size; if (count > 0x100) count = 0x100; data = gsf_input_read (GSF_INPUT (input), count, NULL); g_return_if_fail (data != NULL); if (dump_as_hex) gsf_mem_dump_full (data, count, offset); else fwrite (data, 1, count, stdout); size -= count; offset += count; } if (!dump_as_hex) fflush (stdout); } /** * gsf_le_get_guint64 * @p: pointer to storage * * Interpret binary data as a guint64 (8 byte unsigned integer type) in little * endian order. * * Returns: interpreted data */ guint64 gsf_le_get_guint64 (void const *p) { #if G_BYTE_ORDER == G_BIG_ENDIAN if (sizeof (guint64) == 8) { guint64 li; int i; guint8 *t = (guint8 *)&li; guint8 *p2 = (guint8 *)p; int sd = sizeof (li); for (i = 0; i < sd; i++) t[i] = p2[sd - 1 - i]; return li; } else { g_error ("Big endian machine, but weird size of guint64"); } #elif G_BYTE_ORDER == G_LITTLE_ENDIAN if (sizeof (guint64) == 8) { /* * On i86, we could access directly, but Alphas require * aligned access. */ guint64 data; memcpy (&data, p, sizeof (data)); return data; } else { g_error ("Little endian machine, but weird size of guint64"); } #else #error "Byte order not recognised -- out of luck" #endif } /** * gsf_le_get_float : * @p: pointer to storage * * Interpret binary data as a float in little endian order. * * * Returns: interpreted data */ float gsf_le_get_float (void const *p) { #if G_FLOAT_BYTE_ORDER == G_BIG_ENDIAN if (sizeof (float) == 4) { float f; int i; guint8 *t = (guint8 *)&f; guint8 *p2 = (guint8 *)p; int sd = sizeof (f); for (i = 0; i < sd; i++) t[i] = p2[sd - 1 - i]; return f; } else { g_error ("Big endian machine, but weird size of floats"); } #elif (G_FLOAT_BYTE_ORDER == G_LITTLE_ENDIAN) || (G_FLOAT_BYTE_ORDER == G_ARMFLOAT_ENDIAN) if (sizeof (float) == 4) { /* * On i86, we could access directly, but Alphas require * aligned access. */ float data; memcpy (&data, p, sizeof (data)); return data; } else { g_error ("Little endian machine, but weird size of floats"); } #else #error "Floating-point byte order not recognised -- out of luck" #endif } /** * gsf_le_set_float : * @p: pointer to storage * @f: float to be stored * * Store a value of type float in memory in little endian order. */ void gsf_le_set_float (void *p, float f) { #if G_FLOAT_BYTE_ORDER == G_BIG_ENDIAN if (sizeof (float) == 4) { int i; guint8 *t = (guint8 *)&f; guint8 *p2 = (guint8 *)p; int sd = sizeof (f); for (i = 0; i < sd; i++) p2[sd - 1 - i] = t[i]; } else { g_error ("Big endian machine, but weird size of floats"); } #elif (G_FLOAT_BYTE_ORDER == G_LITTLE_ENDIAN) || (G_FLOAT_BYTE_ORDER == G_ARMFLOAT_ENDIAN) if (sizeof (float) == 4) { /* * On i86, we could access directly, but Alphas require * aligned access. */ memcpy (p, &f, sizeof (f)); } else { g_error ("Little endian machine, but weird size of floats"); } #else #error "Floating-point byte order not recognised -- out of luck" #endif } /** * gsf_le_get_double : * @p: pointer to storage * * Interpret binary data as a double in little endian order. * * Returns: interpreted data */ double gsf_le_get_double (void const *p) { #if G_FLOAT_BYTE_ORDER == G_ARMFLOAT_ENDIAN double data; memcpy ((char *)&data + 4, p, 4); memcpy ((char *)&data, (char const *)p + 4, 4); return data; #elif G_FLOAT_BYTE_ORDER == G_BIG_ENDIAN if (sizeof (double) == 8) { double d; int i; guint8 *t = (guint8 *)&d; guint8 *p2 = (guint8 *)p; int sd = sizeof (d); for (i = 0; i < sd; i++) t[i] = p2[sd - 1 - i]; return d; } else { g_error ("Big endian machine, but weird size of doubles"); } #elif G_FLOAT_BYTE_ORDER == G_LITTLE_ENDIAN if (sizeof (double) == 8) { /* * On i86, we could access directly, but Alphas require * aligned access. */ double data; memcpy (&data, p, sizeof (data)); return data; } else { g_error ("Little endian machine, but weird size of doubles"); } #else #error "Floating-point byte order not recognised -- out of luck" #endif } /** * gsf_le_set_double : * @p: pointer to storage * @d: double to be stored * * Store a value of type double in memory in little endian order */ void gsf_le_set_double (void *p, double d) { #if G_FLOAT_BYTE_ORDER == G_ARMFLOAT_ENDIAN memcpy (p, (char const *)&d + 4, 4); memcpy ((char *)p + 4, &d, 4); #elif G_FLOAT_BYTE_ORDER == G_BIG_ENDIAN if (sizeof (double) == 8) { int i; guint8 *t = (guint8 *)&d; guint8 *p2 = (guint8 *)p; int sd = sizeof (d); for (i = 0; i < sd; i++) p2[sd - 1 - i] = t[i]; } else { g_error ("Big endian machine, but weird size of doubles"); } #elif G_FLOAT_BYTE_ORDER == G_LITTLE_ENDIAN if (sizeof (double) == 8) { /* * On i86, we could access directly, but Alphas require * aligned access. */ memcpy (p, &d, sizeof (d)); } else { g_error ("Little endian machine, but weird size of doubles"); } #else #error "Floating-point byte order not recognised -- out of luck" #endif } /** * gsf_extension_pointer: * @path: A filename or file path. * * Extracts the extension from the end of a filename (the part after the final * '.' in the filename). * * Returns: A pointer to the extension part of the filename, or a * pointer to the end of the string if the filename does not * have an extension. */ char const * gsf_extension_pointer (char const *path) { char const *s, *end; g_return_val_if_fail (path != NULL, NULL); end = path + strlen (path); for (s = end; s > path; ) { s--; if (G_IS_DIR_SEPARATOR (*s)) break; if (*s == '.') return s + 1; } return end; } /** * gsf_iconv_close : * @handle : handle to be closed. * * A utility wrapper to safely close an iconv handle. **/ void gsf_iconv_close (GIConv handle) { if (handle != NULL && handle != ((GIConv)-1)) g_iconv_close (handle); } /** * gsf_filename_to_utf8: * @filename: file name suitable for open(2). * @quoted: if %TRUE, the resulting utf8 file name will be quoted * (unless it is invalid). * * A utility wrapper to make sure filenames are valid utf8. * Caller must g_free the result. * * Returns: @filename using utf-8 encoding for display **/ char * gsf_filename_to_utf8 (char const *filename, gboolean quoted) { char *dname = g_filename_display_name (filename); char *result; if (quoted) { result = g_strconcat ("\"", dname, "\"", NULL); g_free (dname); } else result = dname; return result; } /***************************************************************************/ /* some code taken from evolution/camel/camel-mime-utils.c */ /* * Copyright (C) 2000 Ximian Inc. * * Authors: Michael Zucchi * Jeffrey Stedfast * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ /* dont touch this file without my permission - Michael */ static guint8 camel_mime_base64_rank[256]; static char const *base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; #define d(x) /* Line length for base64 encoding. Must be a multiple of 4. */ enum { BASE64_LINE_LEN = 76 }; static void base64_init(void) { int i; memset(camel_mime_base64_rank, 0xff, sizeof(camel_mime_base64_rank)); for (i=0;i<64;i++) { camel_mime_base64_rank[(unsigned int)base64_alphabet[i]] = i; } camel_mime_base64_rank['='] = 0; } /** * gsf_base64_encode_close : * @in : Data to be encoded * @inlen : Length of data to be encoded * @break_lines : Whether to use line breaks * @out : Encoded data. * @state: holds the number of bits that are stored in @save * @save: leftover bits that have not yet been decoded * * This funcion should be called to when finished encoding everything, to * flush off the last little bit. * * Returns: */ size_t gsf_base64_encode_close (guint8 const *in, size_t inlen, gboolean break_lines, guint8 *out, int *state, unsigned int *save) { int c1, c2; guint8 *outptr = out; if (inlen>0) outptr += gsf_base64_encode_step(in, inlen, break_lines, outptr, state, save); c1 = ((guint8 *)save)[1]; c2 = ((guint8 *)save)[2]; d(printf("mode = %d\nc1 = %c\nc2 = %c\n", (int)((char *)save)[0], (int)((char *)save)[1], (int)((char *)save)[2])); switch (((char *)save)[0]) { case 2: outptr[2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ]; g_assert(outptr[2] != 0); goto skip; case 1: outptr[2] = '='; skip: outptr[0] = base64_alphabet[ c1 >> 2 ]; outptr[1] = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )]; outptr[3] = '='; outptr += 4; ++*state; break; } if (break_lines && *state > 0) *outptr++ = '\n'; *save = 0; *state = 0; return outptr-out; } /** * gsf_base64_encode_step : * @in : input stream * @len : max length of data to decode * @break_lines : Whether to use line breaks * @out : output stream * @state : holds the number of bits that are stored in @save * @save : leftover bits that have not yet been decoded * * Performs an 'encode step', only encodes blocks of 3 characters from @in into * the output @out at a time, saves left-over state in @state and @save * (initialise to 0 on first invocation). * * Returns: the number of bytes encoded */ size_t gsf_base64_encode_step (guint8 const *in, size_t len, gboolean break_lines, guint8 *out, int *state, unsigned int *save) { register guint8 const *inptr; register guint8 *outptr; if (len<=0) return 0; inptr = in; outptr = out; d(printf("we have %d chars, and %d saved chars\n", len, ((char *)save)[0])); if (len + ((char *)save)[0] > 2) { guint8 const *inend = in+len-2; register int c1, c2, c3; register int already; already = *state; switch (((char *)save)[0]) { case 1: c1 = ((guint8 *)save)[1]; goto skip1; case 2: c1 = ((guint8 *)save)[1]; c2 = ((guint8 *)save)[2]; goto skip2; } /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */ while (inptr < inend) { c1 = *inptr++; skip1: c2 = *inptr++; skip2: c3 = *inptr++; *outptr++ = base64_alphabet[ c1 >> 2 ]; *outptr++ = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ]; *outptr++ = base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ]; *outptr++ = base64_alphabet[ c3 & 0x3f ]; /* this is a bit ugly ... */ if (break_lines && (++already) * 4 >= BASE64_LINE_LEN) { *outptr++='\n'; already = 0; } } ((char *)save)[0] = 0; len = 2-(inptr-inend); *state = already; } d(printf("state = %d, len = %d\n", (int)((char *)save)[0], len)); if (len>0) { register char *saveout; /* points to the slot for the next char to save */ saveout = & (((char *)save)[1]) + ((char *)save)[0]; /* len can only be 0 1 or 2 */ switch(len) { case 2: *saveout++ = *inptr++; case 1: *saveout++ = *inptr++; } ((char *)save)[0]+=len; } d(printf("mode = %d\nc1 = %c\nc2 = %c\n", (int)((char *)save)[0], (int)((char *)save)[1], (int)((char *)save)[2])); return outptr-out; } /** * gsf_base64_decode_step: decode a chunk of base64 encoded data * @in: input stream * @len: max length of data to decode * @out: output stream * @state: holds the number of bits that are stored in @save * @save: leftover bits that have not yet been decoded * * Decodes a chunk of base64 encoded data * * Returns: the number of bytes converted **/ size_t gsf_base64_decode_step (guint8 const *in, size_t len, guint8 *out, int *state, guint *save) { register guint8 const *inptr; register guint8 *outptr, c; register unsigned int v; guint8 const *inend; int i; inend = in+len; outptr = out; /* convert 4 base64 bytes to 3 normal bytes */ v=*save; i=*state; inptr = in; while (inptr>16; *outptr++ = v>>8; *outptr++ = v; i=0; } } } *save = v; *state = i; /* quick scan back for '=' on the end somewhere */ /* fortunately we can drop 1 output char for each trailing = (upto 2) */ i=2; while (inptr>in && i) { inptr--; if (camel_mime_base64_rank[*inptr] != 0xff) { if (*inptr == '=' && outptr>out) outptr--; i--; } } /* if i!= 0 then there is a truncation error! */ return outptr-out; } /** * gsf_base64_encode_simple : * @data : data stream * @len : max length of data to encode * * Encodes data from @data back into @data using base64 encoding. * * Returns: the number of bytes encoded */ guint8 * gsf_base64_encode_simple (guint8 const *data, size_t len) { guint8 *out; int state = 0, outlen; unsigned int save = 0; gboolean break_lines = TRUE; outlen = len * 4 / 3 + 5; if (break_lines) outlen += outlen / BASE64_LINE_LEN + 1; out = g_new (guint8, outlen); outlen = gsf_base64_encode_close (data, len, break_lines, out, &state, &save); out [outlen] = '\0'; return out; } /** * gsf_base64_decode_simple : * @data : data stream * @len : max length of data to decode * * Decodes a chunk of base64 encoded data from @data back into @data. * * Returns: the number of bytes converted */ size_t gsf_base64_decode_simple (guint8 *data, size_t len) { int state = 0; unsigned int save = 0; return gsf_base64_decode_step (data, len, data, &state, &save); } /* Largely a copy of g_object_new_valist. */ /** * gsf_property_settings_collect_valist: collect property setting from a va_list. * @object_type: the GType for which the properties are being set. * @p_n_params: a pointer to the number of properties collected. (Used for * both input and output.) * @p_params: a pointer to the GParameter array that holds the properties. * (Used for both input and output. This may point to a %NULL pointer if * there are no properties collected yet.) * @first_property_name: the name of the first property being set, or NULL. * @var_args: a va_list holding the remainder of the property names and * values, terminated by a %NULL. * * This function builds a GParameter array suitable for g_object_newv. **/ void gsf_property_settings_collect_valist (GType object_type, GParameter **p_params, size_t *p_n_params, const gchar *first_property_name, va_list var_args) { GObjectClass *class; GParameter *params = *p_params; const gchar *name; size_t n_params = *p_n_params; size_t n_alloced_params = n_params; /* We might have more. */ g_return_if_fail (G_TYPE_IS_OBJECT (object_type)); class = g_type_class_ref (object_type); name = first_property_name; while (name) { gchar *error = NULL; GParamSpec *pspec = g_object_class_find_property (class, name); if (!pspec) { g_warning ("%s: object class `%s' has no property named `%s'", G_STRFUNC, g_type_name (object_type), name); break; } if (n_params >= n_alloced_params) { n_alloced_params += 16; params = g_renew (GParameter, params, n_alloced_params); } params[n_params].name = name; params[n_params].value.g_type = 0; g_value_init (¶ms[n_params].value, G_PARAM_SPEC_VALUE_TYPE (pspec)); G_VALUE_COLLECT (¶ms[n_params].value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRFUNC, error); g_free (error); g_value_unset (¶ms[n_params].value); break; } n_params++; name = va_arg (var_args, gchar*); } g_type_class_unref (class); *p_params = params; *p_n_params = n_params; } /* This is a vararg version of gsf_property_settings_collect_valist. */ void gsf_property_settings_collect (GType object_type, GParameter **p_params, size_t *p_n_params, const gchar *first_property_name, ...) { va_list var_args; va_start (var_args, first_property_name); gsf_property_settings_collect_valist (object_type, p_params, p_n_params, first_property_name, var_args); va_end (var_args); } void gsf_property_settings_free (GParameter *params, size_t n_params) { while (n_params--) g_value_unset (¶ms[n_params].value); g_free (params); } /* Errors */ /** * gsf_error_quark: * * Returns: the #GQuark used to identify libgsf errors in #GError structures. * Specific error codes come from the #GsfError enumeration. **/ GQuark gsf_error_quark (void) { static GQuark quark; if (quark == 0) quark = g_quark_from_static_string ("gsf-error-quark"); return quark; }