Code

Translations. French translation minor update.
[inkscape.git] / inkjar / jar.cpp
1 /*
2  * Copyright (C) 1999, 2000  Bryan Burns
3  * Copyright (C) 2004 Johan Ceuppens
4  *
5  * Released under GNU GPL, read the file 'COPYING' for more information
6  */
8 /*
9  * TODO/FIXME: 
10  * - configure #ifdefs should be enabled
11  * - move to cstdlib instead of stdlib.h etc.
12  * - remove exit functions
13  * - move to clean C++ code
14  * - windowsify
15  * - remove a few g_free/g_mallocs
16  * - unseekable files
17  * - move to LGPL by rewriting macros
18  * - crcs for compressed files
19  * - put in eof
20  */
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
27 //#ifdef STDC_HEADERS
28 //#endif
30 //#ifdef HAVE_UNISTD_H
31 //#endif
33 //#ifdef HAVE_SYS_PARAM_H
34 //#else
35 //#define MAXPATHLEN 1024
36 //#endif
38 //#ifdef HAVE_DIRENT_H
39 //#endif
41 //#ifdef HAVE_FCNTL_H
42 #include <fcntl.h>
43 //#endif
45 #include <cstring>
46 #include <string>
47 #include <cstdlib>
48 #include <glib.h>
50 #include "jar.h"
52 #include <fstream>
53 #ifdef WORDS_BIGENDIAN
55 #define L2BI(l) ((l & 0xff000000) >> 24) | \
56 ((l & 0x00ff0000) >> 8)  | \
57 ((l & 0x0000ff00) << 8)  | \
58 ((l & 0x000000ff) << 24);
60 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
62 #endif
64 namespace Inkjar {
66 JarFile::JarFile(gchar const*new_filename)
67 {
68     _filename = strdup(new_filename);
69     _last_filename = NULL;
70     fd = -1;
71 }
73 //fixme: the following should probably just return a const gchar* and not 
74 //       use strdup
75 gchar *JarFile::get_last_filename() const
76 {
77     return (_last_filename != NULL ? strdup(_last_filename) : NULL);
78 }
80 JarFile::~JarFile()
81
82     if (_filename != NULL)
83         g_free(_filename); 
84     if (_last_filename != NULL)
85         g_free(_last_filename); 
86 }
88 bool JarFile::init_inflation()
89 {
90     memset(&_zs, 0, sizeof(z_stream));
91     
92     _zs.zalloc = Z_NULL;
93     _zs.zfree = Z_NULL;
94     _zs.opaque = Z_NULL;
95     
96     if(inflateInit2(&_zs, -15) != Z_OK) {
97         fprintf(stderr,"error initializing inflation!\n");
98         return false;
99     }
101     return true;
104 bool JarFile::open()
106     if ((fd = ::open(_filename, O_RDONLY)) < 0) {
107         fprintf(stderr, "open failed.\n");
108         return false;
109     }
110     if (!init_inflation())
111         return false;
113     return true;
116 bool JarFile::close()
118     if (fd >= 0 && !::close(fd)) {
119         inflateEnd(&_zs);
120         return true;
121     }
122     return false;
125 bool JarFile::read_signature()
127     guint8 *bytes = (guint8 *)g_malloc(sizeof(guint8) * 4);
128     if (!read(bytes, 4)) {
129         g_free(bytes);
130         return false;
131     }
133     guint32 signature = UNPACK_UB4(bytes, 0);
134     g_free(bytes);
136 #ifdef DEBUG
137     std::printf("signature is %x\n", signature);
138 #endif
140     if (signature == 0x08074b50) {
141         //skip data descriptor
142         bytes = (guint8 *)malloc(sizeof(guint8) * 12);
143         if (!read(bytes, 12)) {
144             g_free(bytes);
145             return false;
146         }
147     } else if (signature == 0x02014b50 || signature == 0x04034b50) {
148         return true;
149     } else {
150         return false;
151     }
152     return false;
155 guint32 JarFile::get_crc(guint8 *bytes, guint16 flags)
157     guint32 crc = 0;
158     //no data descriptor
159     if (!(flags & 0x0008)) {
160         crc = UNPACK_UB4(bytes, LOC_CRC);
161         
162 #ifdef DEBUG
163         std::printf("CRC from file is %x\n", crc);
164 #endif
165     }
166     
167     return crc;
170 guint8 *JarFile::read_filename(guint16 filename_length)
172     guint8 *filename = (guint8 *)g_malloc(sizeof(guint8)
173                                           * (filename_length+1));
174     if (!read(filename, filename_length)) {
175         g_free(filename);
176         return NULL;
177     }
178     filename[filename_length] = '\0';
180 #ifdef DEBUG
181     std::printf("Filename is %s\n", filename);
182 #endif
184     return filename;
187 bool JarFile::check_compression_method(guint16 method, guint16 flags)
189     return !(method != 8 && flags & 0x0008);
192 GByteArray *JarFile::get_next_file_contents()
194     guint8 *bytes;
195     GByteArray *gba = g_byte_array_new();
197     read_signature();
198     
199     //get compressed size
200     bytes = (guint8 *)g_malloc(sizeof(guint8) * 30);
201     if (!read(bytes+4, 26)) {
202         g_free(bytes);
203         return NULL;
204     }
205     guint32 compressed_size = UNPACK_UB4(bytes, LOC_CSIZE);
206     guint16 filename_length = UNPACK_UB2(bytes, LOC_FNLEN);
207     guint16 eflen = UNPACK_UB2(bytes, LOC_EFLEN);
208     guint16 flags = UNPACK_UB2(bytes, LOC_EXTRA);
209     guint16 method = UNPACK_UB2(bytes, LOC_COMP);
211     if (filename_length == 0) {
212         g_byte_array_free(gba, TRUE);
213         if (_last_filename != NULL)
214             g_free(_last_filename);
215         _last_filename = NULL;
216         return NULL;
217     }
220 #ifdef DEBUG    
221     std::printf("Compressed size is %u\n", compressed_size);
222     std::printf("Filename length is %hu\n", filename_length);
223     std::printf("Extra field length is %hu\n", eflen);
224     std::printf("Flags are %#hx\n", flags);
225     std::printf("Compression method is %#hx\n", method);
226 #endif
227     
228     guint32 crc = get_crc(bytes, flags);
229     
230     gchar *filename = (gchar *)read_filename(filename_length);
231     g_free(bytes);
232     
233     if (filename == NULL) 
234         return NULL;
235    
236     if (_last_filename != NULL)
237         g_free(_last_filename);
238     _last_filename = filename;
240     //check if this is a directory and skip
241     
242     char *c_ptr;
243     if ((c_ptr = std::strrchr(filename, '/')) != NULL) {
244         if (*(++c_ptr) == '\0') {
245             return NULL;
246         }
247     }
248    
249     if (!check_compression_method(method, flags)) {
250         std::fprintf(stderr, "error in jar file\n");
251         return NULL;
252     }    
253     
254     if (method == 8 || flags & 0x0008) {
255         unsigned int file_length = 0;//uncompressed file length
256         lseek(fd, eflen, SEEK_CUR);
257         guint8 *file_data = get_compressed_file(compressed_size, file_length, 
258                                                 crc, flags);
259         if (file_data == NULL) {
260             g_byte_array_free(gba, FALSE);
261             return NULL;
262         }
263         g_byte_array_append(gba, file_data, file_length);
264     } else if (method == 0) {
265         guint8 *file_data = get_uncompressed_file(compressed_size, crc, 
266                                                   eflen, flags); 
268         if (file_data == NULL) {
269             g_byte_array_free(gba, TRUE);
270             return NULL;
271         }
272         g_byte_array_append(gba, file_data, compressed_size);
273     } else {
274         lseek(fd, compressed_size+eflen, SEEK_CUR);
275         g_byte_array_free(gba, FALSE);
276         return NULL;
277     }
278         
279     
280     return gba;
283 guint8 *JarFile::get_uncompressed_file(guint32 compressed_size, guint32 crc, 
284                                        guint16 eflen, guint16 flags)
286     GByteArray *gba = g_byte_array_new();
287     unsigned int out_a = 0;
288     unsigned int in_a = compressed_size;
289     guint8 *bytes;
290     guint32 crc2 = 0;
291     
292     crc2 = crc32(crc2, NULL, 0);
293     
294     bytes = (guint8 *)g_malloc(sizeof(guint8) * RDSZ);
295     while(out_a < compressed_size){
296         unsigned int nbytes = (in_a > RDSZ ? RDSZ : in_a);
297         
298         if (!(nbytes = read(bytes, nbytes))) {
299             g_free(bytes);
300             return NULL;
301         }
302         
303         crc2 = crc32(crc2, (Bytef*)bytes, nbytes);
304             
305         g_byte_array_append (gba, bytes, nbytes);
306         out_a += nbytes;
307         in_a -= nbytes;
308             
309 #ifdef DEBUG    
310         std::printf("%d bytes written\n", out_a);
311 #endif
312     }
313     lseek(fd, eflen, SEEK_CUR);
314     g_free(bytes);
316     if (!check_crc(crc, crc2, flags)) {
317         bytes = gba->data;
318         g_byte_array_free(gba, FALSE);//FALSE argument does not free actual data
319         return NULL;
320     }
321     
322     return bytes;
325 int JarFile::read(guint8 *buf, int count)
327     int nbytes;
328     if ((nbytes = ::read(fd, buf, count)) != count) {
329         fprintf(stderr, "read error\n");
330         exit(1);
331         return 0;
332     }
333     return nbytes;
336 /* FIXME: this could probably use ZlibBuffer */
337 guint8 *JarFile::get_compressed_file(guint32 compressed_size, 
338                                      unsigned int& file_length,
339                                      guint32 oldcrc, guint16 flags)
341     if (compressed_size == 0)
342         return NULL;
343     
344     guint8 in_buffer[RDSZ];
345     guint8 out_buffer[RDSZ];
346     int nbytes;
347     unsigned int leftover_in = compressed_size;
348     GByteArray *gba = g_byte_array_new();
349     
350     _zs.avail_in = 0;
351     guint32 crc = crc32(0, Z_NULL, 0);
352     
353     do {
354                 
355         if (!_zs.avail_in) {
356         
357             if ((nbytes = ::read(fd, in_buffer, 
358                                  (leftover_in < RDSZ ? leftover_in : RDSZ))) 
359                 < 0) {
360                 fprintf(stderr, "jarfile read error");
361             }
362             _zs.avail_in = nbytes;
363             _zs.next_in = in_buffer;
364             crc = crc32(crc, in_buffer, _zs.avail_in);
365             leftover_in -= RDSZ;
366         }
367         _zs.next_out = out_buffer;
368         _zs.avail_out = RDSZ;
369         
370         int ret = inflate(&_zs, Z_NO_FLUSH);
371         if (RDSZ != _zs.avail_out) {
372             unsigned int tmp_len = RDSZ - _zs.avail_out;
373             guint8 *tmp_bytes = (guint8 *)g_malloc(sizeof(guint8) 
374                                                    * tmp_len);
375             memcpy(tmp_bytes, out_buffer, tmp_len);
376             g_byte_array_append(gba, tmp_bytes, tmp_len);
377         }
378         
379         if (ret == Z_STREAM_END) {
380             break;
381         }
382         if (ret != Z_OK)
383             std::printf("decompression error %d\n", ret);
384     } while (_zs.total_in < compressed_size);
385     
386     file_length = _zs.total_out;
387 #ifdef DEBUG
388     std::printf("done inflating\n");
389     std::printf("%d bytes left over\n", _zs.avail_in);
390     std::printf("CRC is %x\n", crc);
391 #endif
392     
393     guint8 *ret_bytes;
394     if (check_crc(oldcrc, crc, flags) && gba->len > 0)
395         ret_bytes = gba->data;
396     else 
397         ret_bytes = NULL;
398     g_byte_array_free(gba, FALSE);
400     inflateReset(&_zs); 
401     return ret_bytes;
404 bool JarFile::check_crc(guint32 oldcrc, guint32 crc, guint16 flags)
406     //fixme: does not work yet
407         
408     if(flags & 0x0008) {
409         guint8 *bytes = (guint8 *)g_malloc(sizeof(guint8) * 16);
410         if (!read(bytes, 16)) {
411             g_free(bytes);
412             return false;
413         }
414         
415         guint32 signature = UNPACK_UB4(bytes, 0);
416         g_free(bytes);
417         if(signature != 0x08074b50) {
418             fprintf(stderr, "missing data descriptor!\n");
419         }
420         
421         crc = UNPACK_UB4(bytes, 4);
422         
423     }
424     if (oldcrc != crc) {
425 #ifdef DEBUG
426         std::fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
427                      oldcrc, crc);
428 #endif
429     }
430     return true;
433 JarFile::JarFile(JarFile const& rhs)
435     *this = rhs;
438 JarFile& JarFile::operator=(JarFile const& rhs)
440     if (this == &rhs)
441         return *this;
443     _zs = rhs._zs;//fixme
444     if (_filename == NULL)
445         _filename = NULL;
446     else
447         _filename = strdup(rhs._filename);
448     if (_last_filename == NULL)
449         _last_filename = NULL;
450     else 
451         _last_filename = strdup(rhs._last_filename);
452     fd = rhs.fd;
453     
454     return *this;
458 /////////////////////////
459 //      JarFileReader  //
460 /////////////////////////
462 GByteArray *JarFileReader::get_next_file()
464     if (_state == CLOSED) {
465         _jarfile.open();
466         _state = OPEN;
467     }
468     
469     return _jarfile.get_next_file_contents();
472 JarFileReader& JarFileReader::operator=(JarFileReader const& rhs)
474     if (&rhs == this)
475         return *this;
477     _jarfile = rhs._jarfile;
478     _state = rhs._state;
479     
480     return *this;
483 /*
484  * If the filename gets reset, a jarfile object gets generated again,
485  * ready to be opened for reading.
486  */
487 void JarFileReader::set_filename(gchar const *new_filename)
489     _jarfile.close();
490     _jarfile = JarFile(new_filename);
493 void JarFileReader::set_jarfile(JarFile const& new_jarfile)
495     _jarfile = new_jarfile;
498 JarFileReader::JarFileReader(JarFileReader const& rhs)
500     *this = rhs;
503 } // namespace Inkjar
506 #if 0 //testing code
507 #include "jar.h"
508 /*
509  * This program writes all the files from a jarfile to stdout and inflates
510  * where needed.
511  */
512 int main(int argc, char *argv[])
514     gchar *filename;
515     if (argc < 2) {
516         filename = "./ide.jar\0";
517     } else {
518         filename = argv[1];
519     }
521     Inkjar::JarFileReader jar_file_reader(filename);
522     
523     for (;;) {
524         GByteArray *gba = jar_file_reader.get_next_file();
525         if (gba == NULL) {
526             char *c_ptr;
527             gchar *last_filename = jar_file_reader.get_last_filename();
528             if (last_filename == NULL)
529                 break;
530             if ((c_ptr = std::strrchr(last_filename, '/')) != NULL) {
531                 if (*(++c_ptr) == '\0') {
532                     g_free(last_filename);
533                     continue;
534                 }
535             }
536         } else if (gba->len > 0)
537             ::write(1, gba->data, gba->len);
538         else
539             break;
540     }
541     return 0;
543 #endif