]> git.street.me.uk Git - andy/viking.git/blob - src/compression.c
SF Support Requests#21: Make default print size full page.
[andy/viking.git] / src / compression.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4  *
5  * Copyright (C) 2003-2008, Evan Battaglia <gtoevan@gmx.net>
6  * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7  * Copyright (C) 2013, Rob Norris <rw_norris@hotmail.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #ifdef HAVE_LIBZ
30 #include <zlib.h>
31 #endif
32
33 #ifdef HAVE_BZLIB_H
34 #include <bzlib.h>
35 #endif
36
37 #include "compression.h"
38 #include <gio/gio.h>
39 #include <glib/gstdio.h>
40
41 #ifdef HAVE_LIBZ
42 /* return size of unzip data or 0 if failed */
43 static guint uncompress_data(void *uncompressed_buffer, guint uncompressed_size, void *compressed_data, guint compressed_size)
44 {
45         z_stream stream;
46         int err;
47
48         stream.next_in = compressed_data;
49         stream.avail_in = compressed_size;
50         stream.next_out = uncompressed_buffer;
51         stream.avail_out = uncompressed_size;
52         stream.zalloc = (alloc_func)0;
53         stream.zfree = (free_func)0;
54         stream.opaque = (voidpf)0;
55
56         /* negative windowBits to inflateInit2 means "no header" */
57         if ((err = inflateInit2(&stream, -MAX_WBITS)) != Z_OK) {
58                 g_warning("%s(): inflateInit2 failed", __PRETTY_FUNCTION__);
59                 return 0;
60         }
61
62         err = inflate(&stream, Z_FINISH);
63         if ((err != Z_OK) && (err != Z_STREAM_END) && stream.msg) {
64                 g_warning("%s() inflate failed err=%d \"%s\"", __PRETTY_FUNCTION__, err, stream.msg == NULL ? "unknown" : stream.msg);
65                 inflateEnd(&stream);
66                 return 0;
67         }
68
69         inflateEnd(&stream);
70         return(stream.total_out);
71 }
72 #endif
73
74 /**
75  * unzip_file:
76  * @zip_file:   pointer to start of compressed data
77  * @unzip_size: the size of the compressed data block
78  *
79  * Returns a pointer to uncompressed data (maybe NULL)
80  */
81 void *unzip_file(gchar *zip_file, gulong *unzip_size)
82 {
83         void *unzip_data = NULL;
84 #ifndef HAVE_LIBZ
85         goto end;
86 #else
87         gchar *zip_data;
88         struct _lfh {
89                 guint32 sig;
90                 guint16 extract_version;
91                 guint16 flags;
92                 guint16 comp_method;
93                 guint16 time;
94                 guint16 date;
95                 guint32 crc_32;
96                 guint32 compressed_size;
97                 guint32 uncompressed_size;
98                 guint16 filename_len;
99                 guint16 extra_field_len;
100         }  __attribute__ ((__packed__)) *local_file_header = NULL;
101
102
103         local_file_header = (struct _lfh *) zip_file;
104         if (GUINT32_FROM_LE(local_file_header->sig) != 0x04034b50) {
105                 g_warning("%s(): wrong format", __PRETTY_FUNCTION__);
106                 g_free(unzip_data);
107                 goto end;
108         }
109
110         zip_data = zip_file + sizeof(struct _lfh)
111                 + GUINT16_FROM_LE(local_file_header->filename_len)
112                 + GUINT16_FROM_LE(local_file_header->extra_field_len);
113         gulong uncompressed_size = GUINT32_FROM_LE(local_file_header->uncompressed_size);
114         unzip_data = g_malloc(uncompressed_size);
115
116         if (!(*unzip_size = uncompress_data(unzip_data, uncompressed_size, zip_data, GUINT32_FROM_LE(local_file_header->compressed_size)))) {
117                 g_free(unzip_data);
118                 unzip_data = NULL;
119                 goto end;
120         }
121
122 #endif
123 end:
124         return(unzip_data);
125 }
126
127 /**
128  * uncompress_bzip2:
129  * @name: The name of the file to attempt to decompress
130  *
131  * Returns: The name of the uncompressed file (in a temporary location) or NULL
132  *   free the returned name after use.
133  *
134  * Also see: http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html
135  */
136 gchar* uncompress_bzip2 ( gchar *name )
137 {
138 #ifdef HAVE_BZLIB_H
139         FILE *ff = g_fopen ( name, "r" );
140         if ( !ff )
141                 return NULL;
142
143         int     bzerror;
144         BZFILE* bf = BZ2_bzReadOpen ( &bzerror, ff, 0, 0, NULL, 0 ); // This should take care of the bz2 file header
145         if ( bzerror != BZ_OK ) {
146                 BZ2_bzReadClose ( &bzerror, bf );
147                 // handle error
148                 g_warning ( "%s: BZ ReadOpen error on %s", __FUNCTION__, name );
149                 return NULL;
150         }
151
152         GFileIOStream *gios;
153         GError *error = NULL;
154         gchar *tmpname = NULL;
155 #if GLIB_CHECK_VERSION(2,32,0)
156         GFile *gf = g_file_new_tmp ( "vik-bz2-tmp.XXXXXX", &gios, &error );
157         tmpname = g_file_get_path (gf);
158 #else
159         gint fd = g_file_open_tmp ( "vik-bz2-tmp.XXXXXX", &tmpname, &error );
160         if ( error ) {
161                 g_warning ( error->message );
162                 g_error_free ( error );
163                 return NULL;
164         }
165         gios = g_file_open_readwrite ( g_file_new_for_path (tmpname), NULL, &error );
166         if ( error ) {
167                 g_warning ( error->message );
168                 g_error_free ( error );
169                 return NULL;
170         }
171 #endif
172
173         GOutputStream *gos = g_io_stream_get_output_stream ( G_IO_STREAM(gios) );
174
175         // Process in arbitary sized chunks
176         char buf[4096];
177         bzerror = BZ_OK;
178         int nBuf;
179         // Now process the actual compression data
180         while ( bzerror == BZ_OK ) {
181                 nBuf = BZ2_bzRead ( &bzerror, bf, buf, 4096 );
182                 if ( bzerror == BZ_OK || bzerror == BZ_STREAM_END) {
183                         // do something with buf[0 .. nBuf-1]
184                         if ( g_output_stream_write ( gos, buf, nBuf, NULL, &error ) < 0 ) {
185                                 g_critical ( "Couldn't write bz2 tmp %s file due to %s", tmpname, error->message );
186                                 g_error_free (error);
187                                 BZ2_bzReadClose ( &bzerror, bf );
188                                 goto end;
189                         }
190                 }
191         }
192         if ( bzerror != BZ_STREAM_END ) {
193                 // handle error...
194                 g_warning ( "%s: BZ error :( %d", __FUNCTION__, bzerror );
195         }
196         BZ2_bzReadClose ( &bzerror, bf );
197         g_output_stream_close ( gos, NULL, &error );
198
199  end:
200         return tmpname;
201 #else
202         return NULL;
203 #endif
204 }