diff options
author | Christian Grothoff <christian@grothoff.org> | 2012-08-04 21:46:54 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2012-08-04 21:46:54 +0000 |
commit | c108c43a6e8a3a09fcb7a9d4d74f85076d33ad2f (patch) | |
tree | eaada8a4bd84e072c0b2ab2a56579054361a88c9 | |
parent | a1f2f478e5f939c574cc05460030336761177dd9 (diff) | |
download | libextractor-c108c43a6e8a3a09fcb7a9d4d74f85076d33ad2f.tar.gz libextractor-c108c43a6e8a3a09fcb7a9d4d74f85076d33ad2f.zip |
adding test for gif extractor
-rw-r--r-- | src/plugins/Makefile.am | 26 | ||||
-rw-r--r-- | src/plugins/gif_extractor.c | 327 | ||||
-rw-r--r-- | src/plugins/mime_extractor.c | 2 | ||||
-rw-r--r-- | src/plugins/test_gif.c | 77 |
4 files changed, 187 insertions, 245 deletions
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index fe847db..447b674 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am | |||
@@ -25,9 +25,15 @@ PLUGIN_MIME=libextractor_mime.la | |||
25 | TEST_MIME=test_mime | 25 | TEST_MIME=test_mime |
26 | endif | 26 | endif |
27 | 27 | ||
28 | if HAVE_GIF | ||
29 | PLUGIN_GIF=libextractor_gif.la | ||
30 | TEST_GIF=test_gif | ||
31 | endif | ||
32 | |||
28 | plugin_LTLIBRARIES = \ | 33 | plugin_LTLIBRARIES = \ |
29 | $(PLUGIN_OGG) \ | 34 | $(PLUGIN_OGG) \ |
30 | $(PLUGIN_MIME) | 35 | $(PLUGIN_MIME) \ |
36 | $(PLUGIN_GIF) | ||
31 | 37 | ||
32 | if HAVE_ZZUF | 38 | if HAVE_ZZUF |
33 | fuzz_tests=fuzz_default.sh | 39 | fuzz_tests=fuzz_default.sh |
@@ -35,7 +41,8 @@ endif | |||
35 | 41 | ||
36 | check_PROGRAMS = \ | 42 | check_PROGRAMS = \ |
37 | $(TEST_OGG) \ | 43 | $(TEST_OGG) \ |
38 | $(TEST_MIME) | 44 | $(TEST_MIME) \ |
45 | $(TEST_GIF) | ||
39 | 46 | ||
40 | TESTS = \ | 47 | TESTS = \ |
41 | $(fuzz_tests) \ | 48 | $(fuzz_tests) \ |
@@ -81,4 +88,19 @@ test_mime_LDADD = \ | |||
81 | $(top_builddir)/src/plugins/libtest.la | 88 | $(top_builddir)/src/plugins/libtest.la |
82 | 89 | ||
83 | 90 | ||
91 | libextractor_gif_la_SOURCES = \ | ||
92 | gif_extractor.c | ||
93 | libextractor_gif_la_LDFLAGS = \ | ||
94 | $(PLUGINFLAGS) | ||
95 | libextractor_gif_la_LIBADD = \ | ||
96 | $(top_builddir)/src/main/libextractor.la \ | ||
97 | $(top_builddir)/src/common/libextractor_common.la \ | ||
98 | -lgif | ||
99 | |||
100 | test_gif_SOURCES = \ | ||
101 | test_gif.c | ||
102 | test_gif_LDADD = \ | ||
103 | $(top_builddir)/src/plugins/libtest.la | ||
104 | |||
105 | |||
84 | 106 | ||
diff --git a/src/plugins/gif_extractor.c b/src/plugins/gif_extractor.c index 055fd9f..1e85f87 100644 --- a/src/plugins/gif_extractor.c +++ b/src/plugins/gif_extractor.c | |||
@@ -1,10 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | This file is part of libextractor. | 2 | This file is part of libextractor. |
3 | (C) 2002, 2003, 2009 Vidyut Samanta and Christian Grothoff | 3 | (C) 2012 Vidyut Samanta and Christian Grothoff |
4 | 4 | ||
5 | libextractor is free software; you can redistribute it and/or modify | 5 | libextractor is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published | 6 | it under the terms of the GNU General Public License as published |
7 | by the Free Software Foundation; either version 2, or (at your | 7 | by the Free Software Foundation; either version 3, or (at your |
8 | option) any later version. | 8 | option) any later version. |
9 | 9 | ||
10 | libextractor is distributed in the hope that it will be useful, but | 10 | libextractor is distributed in the hope that it will be useful, but |
@@ -20,261 +20,104 @@ | |||
20 | 20 | ||
21 | #include "platform.h" | 21 | #include "platform.h" |
22 | #include "extractor.h" | 22 | #include "extractor.h" |
23 | #include "pack.h" | 23 | #include <gif_lib.h> |
24 | 24 | ||
25 | #define DEBUG_GIF 0 | ||
26 | #if DEBUG_GIF | ||
27 | #define PRINT(a,b) fprintf(stderr,a,b) | ||
28 | #else | ||
29 | #define PRINT(a,b) | ||
30 | #endif | ||
31 | |||
32 | struct GifHeader | ||
33 | { | ||
34 | char gif[3]; | ||
35 | char version[3]; | ||
36 | uint16_t screen_width; | ||
37 | uint16_t screen_height; | ||
38 | unsigned char flags; | ||
39 | #define HEADER_FLAGS__SIZE_OF_GLOBAL_COLOR_TABLE 0x07 | ||
40 | #define HEADER_FLAGS__SORT_FLAG 0x08 | ||
41 | #define HEADER_FLAGS__COLOR_RESOLUTION 0x70 | ||
42 | #define HEADER_FLAGS__GLOBAL_COLOR_TABLE_FLAG 0x80 | ||
43 | unsigned char background_color_index; | ||
44 | unsigned char pixel_aspect_ratio; | ||
45 | }; | ||
46 | |||
47 | #define GIF_HEADER_SIZE 13 | ||
48 | #define GIF_HEADER_SPEC "3b3bhhbbb" | ||
49 | #define GIF_HEADER_FIELDS(p) \ | ||
50 | &(p)->gif,\ | ||
51 | &(p)->version, \ | ||
52 | &(p)->screen_width, \ | ||
53 | &(p)->screen_height, \ | ||
54 | &(p)->flags, \ | ||
55 | &(p)->background_color_index, \ | ||
56 | &(p)->pixel_aspect_ratio | ||
57 | |||
58 | struct GifDescriptor | ||
59 | { | ||
60 | unsigned char image_separator; | ||
61 | uint16_t image_left; | ||
62 | uint16_t image_top; | ||
63 | uint16_t image_width; | ||
64 | uint16_t image_height; | ||
65 | unsigned char flags; | ||
66 | #define DESCRIPTOR_FLAGS__PIXEL_SIZE 0x07 | ||
67 | #define DESCRIPTOR_FLAGS__RESERVED 0x18 | ||
68 | #define DESCRIPTOR_FLAGS__SORT_FLAG 0x20 | ||
69 | #define DESCRIPTOR_FLAGS__INTERLACE_FLAG 0x40 | ||
70 | #define DESCRIPTOR_FLAGS__LOCAL_COLOR_TABLE_FLAG 0x80 | ||
71 | }; | ||
72 | |||
73 | #define GIF_DESCRIPTOR_SIZE 10 | ||
74 | #define GIF_DESCRIPTOR_SPEC "chhhhc" | ||
75 | #define GIF_DESCRIPTOR_FIELDS(p) \ | ||
76 | &(p)->image_separator, \ | ||
77 | &(p)->image_left, \ | ||
78 | &(p)->image_top, \ | ||
79 | &(p)->image_width, \ | ||
80 | &(p)->image_height, \ | ||
81 | &(p)->flags | ||
82 | |||
83 | struct GifExtension | ||
84 | { | ||
85 | unsigned char extension_introducer; | ||
86 | unsigned char graphic_control_label; | ||
87 | }; | ||
88 | |||
89 | /** | ||
90 | * Skip a data block. | ||
91 | * @return the position after the block | ||
92 | */ | ||
93 | static size_t | ||
94 | skipDataBlock (const unsigned char *data, size_t pos, const size_t size) | ||
95 | { | ||
96 | while ((pos < size) && (data[pos] != 0)) | ||
97 | pos += data[pos] + 1; | ||
98 | return pos + 1; | ||
99 | } | ||
100 | 25 | ||
101 | /** | 26 | /** |
102 | * skip an extention block | 27 | * Callback invoked by libgif to read data. |
103 | * @return the position after the block | 28 | * |
29 | * @param ft the file handle, including our extract context | ||
30 | * @param bt where to write the data | ||
31 | * @param arg number of bytes to read | ||
32 | * @return -1 on error, otherwise number of bytes read | ||
104 | */ | 33 | */ |
105 | static size_t | 34 | static int |
106 | skipExtensionBlock (const unsigned char *data, | 35 | gif_read_func (GifFileType *ft, |
107 | size_t pos, const size_t size, | 36 | GifByteType *bt, |
108 | const struct GifExtension * ext) | 37 | int arg) |
109 | { | 38 | { |
110 | return skipDataBlock (data, pos + sizeof (struct GifExtension), size); | 39 | struct EXTRACTOR_ExtractContext *ec = ft->UserData; |
40 | void *data; | ||
41 | ssize_t ret; | ||
42 | |||
43 | ret = ec->read (ec->cls, | ||
44 | &data, | ||
45 | arg); | ||
46 | if (-1 == ret) | ||
47 | return -1; | ||
48 | memcpy (bt, data, ret); | ||
49 | return ret; | ||
111 | } | 50 | } |
112 | 51 | ||
113 | /** | ||
114 | * @return the offset after the global color map | ||
115 | */ | ||
116 | static size_t | ||
117 | skipGlobalColorMap (const unsigned char *data, | ||
118 | const size_t size, | ||
119 | const struct GifHeader * header) | ||
120 | { | ||
121 | size_t gct_size; | ||
122 | |||
123 | if ((header->flags & HEADER_FLAGS__GLOBAL_COLOR_TABLE_FLAG) > 0) | ||
124 | gct_size = | ||
125 | 3 * | ||
126 | (1 << ((header->flags & HEADER_FLAGS__SIZE_OF_GLOBAL_COLOR_TABLE) + 1)); | ||
127 | else | ||
128 | gct_size = 0; | ||
129 | return GIF_HEADER_SIZE + gct_size; | ||
130 | } | ||
131 | 52 | ||
132 | /** | 53 | /** |
133 | * @return the offset after the local color map | 54 | * Main entry method for the 'image/gif' extraction plugin. |
55 | * | ||
56 | * @param ec extraction context provided to the plugin | ||
134 | */ | 57 | */ |
135 | static size_t | 58 | void |
136 | skipLocalColorMap (const unsigned char *data, | 59 | EXTRACTOR_gif_extract_method (struct EXTRACTOR_ExtractContext *ec) |
137 | size_t pos, const size_t size, | ||
138 | const struct GifDescriptor * descriptor) | ||
139 | { | 60 | { |
140 | size_t lct_size; | 61 | GifFileType *gif_file; |
141 | 62 | GifRecordType gif_type; | |
142 | if (pos + GIF_DESCRIPTOR_SIZE > size) | 63 | GifByteType *ext; |
143 | return size; | 64 | int et; |
144 | if ((descriptor->flags & DESCRIPTOR_FLAGS__LOCAL_COLOR_TABLE_FLAG) > 0) | 65 | char dims[128]; |
145 | lct_size = | 66 | |
146 | 3 * (1 << ((descriptor->flags & DESCRIPTOR_FLAGS__PIXEL_SIZE) + 1)); | 67 | if (NULL == (gif_file = DGifOpen (ec, &gif_read_func))) |
147 | else | 68 | return; /* not a GIF */ |
148 | lct_size = 0; | 69 | if (0 != |
149 | return pos + GIF_DESCRIPTOR_SIZE + lct_size; | 70 | ec->proc (ec->cls, |
150 | } | 71 | "gif", |
151 | 72 | EXTRACTOR_METATYPE_MIMETYPE, | |
152 | static int | 73 | EXTRACTOR_METAFORMAT_UTF8, |
153 | parseComment (const unsigned char *data, | 74 | "text/plain", |
154 | size_t pos, const size_t size, | 75 | "image/gif", |
155 | EXTRACTOR_MetaDataProcessor proc, | 76 | strlen ("image/gif") + 1)) |
156 | void *proc_cls) | 77 | return; |
157 | { | 78 | snprintf (dims, |
158 | size_t length; | 79 | sizeof (dims), |
159 | size_t off; | 80 | "%dx%d", |
160 | size_t curr = pos; | 81 | gif_file->SHeight, |
161 | int ret; | 82 | gif_file->SWidth); |
162 | 83 | if (0 != | |
163 | length = 0; | 84 | ec->proc (ec->cls, |
164 | while ( (curr < size) && | 85 | "gif", |
165 | (data[curr] != 0) ) | 86 | EXTRACTOR_METATYPE_IMAGE_DIMENSIONS, |
87 | EXTRACTOR_METAFORMAT_UTF8, | ||
88 | "text/plain", | ||
89 | dims, | ||
90 | strlen (dims) + 1)) | ||
91 | return; | ||
92 | while (1) | ||
166 | { | 93 | { |
167 | length += data[curr]; | 94 | if (GIF_OK != |
168 | curr += data[curr] + 1; | 95 | DGifGetRecordType (gif_file, |
169 | if (length > 65536) | 96 | &gif_type)) |
170 | break; | 97 | break; |
171 | } | 98 | if (UNDEFINED_RECORD_TYPE == gif_type) |
172 | if ( (length < 65536) && | 99 | break; |
173 | (curr < size) ) | 100 | if (EXTENSION_RECORD_TYPE != gif_type) |
174 | { | 101 | continue; |
175 | char comment[length+1]; | 102 | if (GIF_OK != |
176 | 103 | DGifGetExtension (gif_file, &et, &ext)) | |
177 | curr = pos; | 104 | continue; |
178 | off = 0; | 105 | if (COMMENT_EXT_FUNC_CODE == et) |
179 | while ((data[curr] != 0) && (curr < size)) | ||
180 | { | 106 | { |
181 | if (off + data[curr] >= size) | 107 | ec->proc (ec->cls, |
182 | break; | 108 | "gif", |
183 | memcpy (&comment[off], | 109 | EXTRACTOR_METATYPE_COMMENT, |
184 | &data[curr] + 1, | 110 | EXTRACTOR_METAFORMAT_C_STRING, |
185 | data[curr]); | 111 | "text/plain", |
186 | off += data[curr]; | 112 | (char*) &ext[1], |
187 | curr += data[curr] + 1; | 113 | (uint8_t) ext[0]); |
114 | break; | ||
188 | } | 115 | } |
189 | comment[off] = '\0'; | 116 | while ( (GIF_ERROR != |
190 | ret = proc (proc_cls, | 117 | DGifGetExtensionNext(gif_file, &ext)) && |
191 | "gif", | 118 | (NULL != ext) ) ; /* keep going */ |
192 | EXTRACTOR_METATYPE_COMMENT, | ||
193 | EXTRACTOR_METAFORMAT_UTF8, | ||
194 | "text/plain", | ||
195 | comment, | ||
196 | length+1); | ||
197 | } | 119 | } |
198 | else | 120 | DGifCloseFile (gif_file); |
199 | { | ||
200 | /* too big */ | ||
201 | ret = 0; | ||
202 | } | ||
203 | return ret; | ||
204 | } | 121 | } |
205 | 122 | ||
206 | 123 | /* end of gif_extractor.c */ | |
207 | int | ||
208 | EXTRACTOR_gif_extract (const unsigned char *data, | ||
209 | size_t size, | ||
210 | EXTRACTOR_MetaDataProcessor proc, | ||
211 | void *proc_cls, | ||
212 | const char *options) | ||
213 | { | ||
214 | size_t pos; | ||
215 | struct GifHeader header; | ||
216 | struct GifDescriptor gd; | ||
217 | char tmp[128]; | ||
218 | |||
219 | if (size < GIF_HEADER_SIZE) | ||
220 | return 0; | ||
221 | EXTRACTOR_common_cat_unpack (data, GIF_HEADER_SPEC, GIF_HEADER_FIELDS (&header)); | ||
222 | if (0 != strncmp (&header.gif[0], "GIF", 3)) | ||
223 | return 0; | ||
224 | if (0 != strncmp (&header.version[0], "89a", 3)) | ||
225 | return 0; /* only 89a has support for comments */ | ||
226 | if (0 != proc (proc_cls, | ||
227 | "gif", | ||
228 | EXTRACTOR_METATYPE_MIMETYPE, | ||
229 | EXTRACTOR_METAFORMAT_UTF8, | ||
230 | "text/plain", | ||
231 | "image/gif", | ||
232 | strlen ("image/gif")+1)) | ||
233 | return 1; | ||
234 | snprintf (tmp, | ||
235 | sizeof(tmp), | ||
236 | "%ux%u", | ||
237 | header.screen_width, header.screen_height); | ||
238 | if (0 != proc (proc_cls, | ||
239 | "gif", | ||
240 | EXTRACTOR_METATYPE_IMAGE_DIMENSIONS, | ||
241 | EXTRACTOR_METAFORMAT_UTF8, | ||
242 | "text/plain", | ||
243 | tmp, | ||
244 | strlen (tmp)+1)) | ||
245 | return 1; | ||
246 | pos = skipGlobalColorMap (data, size, &header); | ||
247 | PRINT ("global color map ends at %d\n", pos); | ||
248 | while (pos < size) | ||
249 | { | ||
250 | switch (data[pos]) | ||
251 | { | ||
252 | case ',': /* image descriptor block */ | ||
253 | PRINT ("skipping local color map %d\n", pos); | ||
254 | EXTRACTOR_common_cat_unpack (&data[pos], | ||
255 | GIF_DESCRIPTOR_SPEC, | ||
256 | GIF_DESCRIPTOR_FIELDS (&gd)); | ||
257 | pos = skipLocalColorMap (data, pos, size, &gd); | ||
258 | break; | ||
259 | case '!': /* extension block */ | ||
260 | PRINT ("skipping extension block %d\n", pos); | ||
261 | if (data[pos + 1] == (unsigned char) 0xFE) | ||
262 | { | ||
263 | if (0 != parseComment (data, pos + 2, size, proc, proc_cls)) | ||
264 | return 1; | ||
265 | } | ||
266 | pos = skipExtensionBlock (data, pos, size, | ||
267 | (const struct GifExtension *) & data[pos]); | ||
268 | break; | ||
269 | case ';': | ||
270 | PRINT ("hit terminator at %d!\n", pos); | ||
271 | return 0; /* terminator! */ | ||
272 | default: /* raster data block */ | ||
273 | PRINT ("skipping data block at %d\n", pos); | ||
274 | pos = skipDataBlock (data, pos + 1, size); | ||
275 | break; | ||
276 | } | ||
277 | } | ||
278 | PRINT ("returning at %d\n", pos); | ||
279 | return 0; | ||
280 | } | ||
diff --git a/src/plugins/mime_extractor.c b/src/plugins/mime_extractor.c index 392429a..592ba5f 100644 --- a/src/plugins/mime_extractor.c +++ b/src/plugins/mime_extractor.c | |||
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | libextractor is free software; you can redistribute it and/or modify | 5 | libextractor is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published | 6 | it under the terms of the GNU General Public License as published |
7 | by the Free Software Foundation; either version 2, or (at your | 7 | by the Free Software Foundation; either version 3, or (at your |
8 | option) any later version. | 8 | option) any later version. |
9 | 9 | ||
10 | libextractor is distributed in the hope that it will be useful, but | 10 | libextractor is distributed in the hope that it will be useful, but |
diff --git a/src/plugins/test_gif.c b/src/plugins/test_gif.c new file mode 100644 index 0000000..8fa8185 --- /dev/null +++ b/src/plugins/test_gif.c | |||
@@ -0,0 +1,77 @@ | |||
1 | /* | ||
2 | This file is part of libextractor. | ||
3 | (C) 2012 Vidyut Samanta and Christian Grothoff | ||
4 | |||
5 | libextractor is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | libextractor is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with libextractor; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | /** | ||
21 | * @file plugins/test_gif.c | ||
22 | * @brief testcase for ogg plugin | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "test_lib.h" | ||
27 | |||
28 | |||
29 | |||
30 | /** | ||
31 | * Main function for the GIF testcase. | ||
32 | * | ||
33 | * @param argc number of arguments (ignored) | ||
34 | * @param argv arguments (ignored) | ||
35 | * @return 0 on success | ||
36 | */ | ||
37 | int | ||
38 | main (int argc, char *argv[]) | ||
39 | { | ||
40 | struct SolutionData gif_image_sol[] = | ||
41 | { | ||
42 | { | ||
43 | EXTRACTOR_METATYPE_MIMETYPE, | ||
44 | EXTRACTOR_METAFORMAT_UTF8, | ||
45 | "text/plain", | ||
46 | "image/gif", | ||
47 | strlen ("image/gif") + 1, | ||
48 | 0 | ||
49 | }, | ||
50 | { | ||
51 | EXTRACTOR_METATYPE_IMAGE_DIMENSIONS, | ||
52 | EXTRACTOR_METAFORMAT_UTF8, | ||
53 | "text/plain", | ||
54 | "4x4", | ||
55 | strlen ("4x4") + 1, | ||
56 | 0 | ||
57 | }, | ||
58 | { | ||
59 | EXTRACTOR_METATYPE_COMMENT, | ||
60 | EXTRACTOR_METAFORMAT_C_STRING, | ||
61 | "text/plain", | ||
62 | "Testing keyword extraction\n", | ||
63 | strlen ("Testing keyword extraction\n"), | ||
64 | 0 | ||
65 | }, | ||
66 | { 0, 0, NULL, NULL, 0, -1 } | ||
67 | }; | ||
68 | struct ProblemSet ps[] = | ||
69 | { | ||
70 | { "testdata/gif_image.gif", | ||
71 | gif_image_sol }, | ||
72 | { NULL, NULL } | ||
73 | }; | ||
74 | return ET_main ("gif", ps); | ||
75 | } | ||
76 | |||
77 | /* end of test_gif.c */ | ||