3 // Copyright (C) 2007 Daniel Hokka Zakrisson <daniel@hozac.com>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2, or (at your option)
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include <sys/types.h>
37 #include "lib/internal.h"
38 #include "pathconfig.h"
40 #define ENSC_WRAPPERS_PREFIX "h2ext: "
41 #define ENSC_WRAPPERS_UNISTD 1
42 #define ENSC_WRAPPERS_FCNTL 1
43 #define ENSC_WRAPPERS_STAT 1
46 #define MAX_PEEK_SIZE 4096
47 #define MIN(a,b) (((a) > (b)) ? (b) : (a))
48 #define STRINGIFY_(x) #x
49 #define STRINGIFY(x) STRINGIFY_(x)
52 /* where the value would be in the file */
68 /* length of the value */
70 /* program to use for extraction */
72 /* should we try to process the contents as well? */
75 struct file_format *next;
77 typedef struct file_format file_format_t;
79 int wrapper_exit_code = 255;
81 #define CMD_HELP 0x4001
82 #define CMD_VERSION 0x4002
86 { "help", no_argument, 0, CMD_HELP },
87 { "version", no_argument, 0, CMD_VERSION },
88 { "desc", required_argument, 0, 'd' },
89 { "silent", no_argument, 0, 'q' },
94 showHelp(int fd, char const *cmd, int res)
96 WRITE_MSG(fd, "Usage:\n ");
99 " -d <descriptions file> <file1> [<file2>...]\n\n"
100 "Please report bugs to " PACKAGE_BUGREPORT "\n");
109 "h2ext " VERSION " -- determines how to extract a file\n"
110 "This program is part of " PACKAGE_STRING "\n\n"
111 "Copyright (C) 2007 Daniel Hokka Zakrisson\n"
112 VERSION_COPYRIGHT_DISCLAIMER);
116 static file_format_t *
117 find_format(file_format_t *head, char *data)
121 for (i = head; i; i = i->next) {
122 switch (i->type & ~(FFT_LE|FFT_BE)) {
124 if (memcmp(i->value.st, data + i->offset, i->len) == 0)
128 if (i->value.sh == *((__typeof__(i->value.sh) *)data + i->offset))
132 if (i->value.lo == *((__typeof__(i->value.lo) *)data + i->offset))
142 process_file(file_format_t *head, const char *file, file_format_t *ret[2])
148 fd = EopenD(file, O_RDONLY, 0);
150 mapping = mmap(NULL, MIN(st.st_size, MAX_PEEK_SIZE), PROT_READ, MAP_SHARED, fd, 0);
151 if (mapping == MAP_FAILED) {
157 ret[0] = find_format(head, mapping);
159 munmap(mapping, MIN(st.st_size, MAX_PEEK_SIZE));
161 if (ret[0] && ret[0]->peek_inside) {
165 Elseek(fd, 0, SEEK_SET);
170 char *argv[3] = { PROG_H2EXT_WORKER, ret[0]->extractor, NULL };
173 EexecvpD(PROG_H2EXT_WORKER, argv);
176 char *buf = calloc(MAX_PEEK_SIZE, sizeof(char)), *cur, *end;
179 /* read MAX_PEEK_SIZE bytes from the decompressor */
181 end = buf + MAX_PEEK_SIZE;
182 while (cur < end && (bytes_read = Eread(fds[0], cur, end - cur - 1)) > 0)
185 /* get rid of the child */
186 kill(child, SIGTERM);
189 ret[1] = find_format(head, buf);
202 byteswap(void *p, size_t len)
206 for (i = 0; i < (len >> 1); i++) {
207 tmp = buf[len - i - 1];
208 buf[len - i - 1] = buf[i];
214 load_description(const char *file, file_format_t **head)
216 file_format_t *prev = NULL,
227 fd = EopenD(file, O_RDONLY, 0);
231 if ((eol = strchr(buf, '\n')) == NULL && (end - buf) < (signed) (sizeof(buf) - 1)) {
232 bytes_read = Eread(fd, end, sizeof(buf) - 1 - (end - buf));
233 /* EOF, implicit newline */
234 if (bytes_read == 0) {
244 else if (eol == NULL) {
245 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX);
248 WRITE_INT(2, line_no);
249 WRITE_MSG(2, " is a really long line\n");
256 if (*buf == '#' || *buf == '\0')
259 i = *head = calloc(1, sizeof(file_format_t));
261 i->next = calloc(1, sizeof(file_format_t));
267 #define get_field() if (*(ptr+1) == '\0') goto new_line_and_free; \
268 for (ptr++; *ptr == '\t' && *ptr != '\0'; ptr++); \
269 for (field = ptr; *ptr != '\t' && *ptr != '\0'; ptr++); \
272 while (*ptr != '\t' && *ptr != '\0')
276 goto new_line_and_free;
277 i->offset = strtol(field, NULL, 0);
280 if (strcmp(field, "string") == 0)
281 i->type = FFT_STRING;
282 else if (strcmp(field, "short") == 0)
284 else if (strcmp(field, "long") == 0)
286 else if (strcmp(field, "leshort") == 0)
287 i->type = FFT_SHORT|FFT_LE;
288 else if (strcmp(field, "beshort") == 0)
289 i->type = FFT_SHORT|FFT_BE;
290 else if (strcmp(field, "lelong") == 0)
291 i->type = FFT_LONG|FFT_LE;
292 else if (strcmp(field, "belong") == 0)
293 i->type = FFT_LONG|FFT_BE;
295 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX);
298 WRITE_INT(2, line_no);
299 WRITE_MSG(2, " has an unknown type: ");
302 goto new_line_and_free;
306 switch (i->type & ~(FFT_BE|FFT_LE)) {
310 i->value.st = tmp = calloc(strlen(field) + 1, sizeof(char));
311 for (c = field; *c; c++) {
314 *(tmp++) = (char)strtol(c + 1, &endptr, 8);
321 i->len = tmp - i->value.st;
325 i->len = sizeof(i->value.sh);
326 i->value.sh = (__typeof__(i->value.sh))strtol(field, NULL, 0);
327 #if BYTE_ORDER != BIG_ENDIAN
328 if (i->type & FFT_BE)
329 #elif BYTE_ORDER != LITTLE_ENDIAN
330 if (i->type & FFT_LE)
332 # error UNKNOWN BYTE ORDER
334 byteswap(&i->value.sh, i->len);
337 i->len = sizeof(i->value.lo);
338 i->value.lo = (__typeof__(i->value.lo))strtol(field, NULL, 0);
339 #if BYTE_ORDER != BIG_ENDIAN
340 if (i->type & FFT_BE)
341 #elif BYTE_ORDER != LITTLE_ENDIAN
342 if (i->type & FFT_LE)
344 # error UNKNOWN BYTE ORDER
346 byteswap(&i->value.lo, i->len);
351 i->extractor = strdup(field);
354 i->peek_inside = (int)strtol(field, NULL, 0);
356 /* sanity check the entry */
358 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX);
361 WRITE_INT(2, line_no);
362 WRITE_MSG(2, " has an invalid offset: ");
363 WRITE_INT(2, i->offset);
365 goto new_line_and_free;
367 else if ((i->offset + i->len) > MAX_PEEK_SIZE) {
368 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX);
371 WRITE_INT(2, line_no);
372 WRITE_MSG(2, " exceeds maximum offset (" STRINGIFY(MAX_PEEK_SIZE) ")\n");
373 goto new_line_and_free;
388 memmove(buf, eol + 1, end - (eol + 1));
389 end = buf + (end - (eol + 1));
396 int main(int argc, char *argv[])
400 file_format_t *head = NULL;
404 int c = getopt_long(argc, argv, "+d:q", CMDLINE_OPTIONS, 0);
408 case CMD_HELP: showHelp(1, argv[0], 0);
409 case CMD_VERSION: showVersion();
410 case 'd': desc = optarg; break;
411 case 'q': quiet = 1; break;
413 WRITE_MSG(2, "Try '");
414 WRITE_STR(2, argv[0]);
415 WRITE_MSG(2, " --help' for more information.\n");
416 return wrapper_exit_code;
421 WRITE_MSG(2, "No descriptions supplied, try '");
422 WRITE_STR(2, argv[0]);
423 WRITE_MSG(2, " --help' for more information.\n");
424 return wrapper_exit_code;
428 if (load_description(desc, &head) == -1)
431 for (file = argv + optind; *file; file++) {
432 file_format_t *formats[2];
437 if (!process_file(head, *file, formats) && formats[0]) {
438 WRITE_STR(1, formats[0]->extractor);
439 if (formats[0]->peek_inside) {
441 WRITE_STR(1, formats[1] ? formats[1]->extractor : "unknown format");
445 WRITE_MSG(1, "unknown format");