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);
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];
213 static inline ALWAYSINLINE void
214 WRITE_INT(int fd, int num)
216 char buf[sizeof(num)*3+2];
219 l = utilvserver_fmt_long(buf,num);
225 load_description(const char *file, file_format_t **head)
227 file_format_t *prev = NULL,
238 fd = EopenD(file, O_RDONLY, 0);
242 if ((eol = strchr(buf, '\n')) == NULL && (end - buf) < (sizeof(buf) - 1)) {
243 bytes_read = Eread(fd, end, sizeof(buf) - 1 - (end - buf));
244 /* EOF, implicit newline */
245 if (bytes_read == 0) {
255 else if (eol == NULL) {
256 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX);
259 WRITE_INT(2, line_no);
260 WRITE_MSG(2, " is a really long line\n");
267 if (*buf == '#' || *buf == '\0')
270 i = *head = calloc(1, sizeof(file_format_t));
272 i->next = calloc(1, sizeof(file_format_t));
278 #define get_field() if (*(ptr+1) == '\0') goto new_line_and_free; \
279 for (ptr++; *ptr == '\t' && *ptr != '\0'; ptr++); \
280 for (field = ptr; *ptr != '\t' && *ptr != '\0'; ptr++); \
283 while (*ptr != '\t' && *ptr != '\0')
287 goto new_line_and_free;
288 i->offset = strtol(field, NULL, 0);
291 if (strcmp(field, "string") == 0)
292 i->type = FFT_STRING;
293 else if (strcmp(field, "short") == 0)
295 else if (strcmp(field, "long") == 0)
297 else if (strcmp(field, "leshort") == 0)
298 i->type = FFT_SHORT|FFT_LE;
299 else if (strcmp(field, "beshort") == 0)
300 i->type = FFT_SHORT|FFT_BE;
301 else if (strcmp(field, "lelong") == 0)
302 i->type = FFT_LONG|FFT_LE;
303 else if (strcmp(field, "belong") == 0)
304 i->type = FFT_LONG|FFT_BE;
306 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX);
309 WRITE_INT(2, line_no);
310 WRITE_MSG(2, " has an unknown type: ");
313 goto new_line_and_free;
317 switch (i->type & ~(FFT_BE|FFT_LE)) {
321 i->value.st = tmp = calloc(strlen(field) + 1, sizeof(char));
322 for (c = field; *c; c++) {
325 *(tmp++) = (char)strtol(c + 1, &endptr, 8);
332 i->len = tmp - i->value.st;
336 i->len = sizeof(i->value.sh);
337 i->value.sh = (__typeof__(i->value.sh))strtol(field, NULL, 0);
338 #if BYTE_ORDER != BIG_ENDIAN
339 if (i->type & FFT_BE)
340 #elif BYTE_ORDER != LITTLE_ENDIAN
341 if (i->type & FFT_LE)
343 # error UNKNOWN BYTE ORDER
345 byteswap(&i->value.sh, i->len);
348 i->len = sizeof(i->value.lo);
349 i->value.lo = (__typeof__(i->value.lo))strtol(field, NULL, 0);
350 #if BYTE_ORDER != BIG_ENDIAN
351 if (i->type & FFT_BE)
352 #elif BYTE_ORDER != LITTLE_ENDIAN
353 if (i->type & FFT_LE)
355 # error UNKNOWN BYTE ORDER
357 byteswap(&i->value.lo, i->len);
362 i->extractor = strdup(field);
365 i->peek_inside = (int)strtol(field, NULL, 0);
367 /* sanity check the entry */
369 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX);
372 WRITE_INT(2, line_no);
373 WRITE_MSG(2, " has an invalid offset: ");
374 WRITE_INT(2, i->offset);
376 goto new_line_and_free;
378 else if ((i->offset + i->len) > MAX_PEEK_SIZE) {
379 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX);
382 WRITE_INT(2, line_no);
383 WRITE_MSG(2, " exceeds maximum offset (" STRINGIFY(MAX_PEEK_SIZE) ")\n");
384 goto new_line_and_free;
399 memmove(buf, eol + 1, end - (eol + 1));
400 end = buf + (end - (eol + 1));
407 int main(int argc, char *argv[])
411 file_format_t *head = NULL;
415 int c = getopt_long(argc, argv, "+d:q", CMDLINE_OPTIONS, 0);
419 case CMD_HELP: showHelp(1, argv[0], 0);
420 case CMD_VERSION: showVersion();
421 case 'd': desc = optarg; break;
422 case 'q': quiet = 1; break;
424 WRITE_MSG(2, "Try '");
425 WRITE_STR(2, argv[0]);
426 WRITE_MSG(2, " --help' for more information.\n");
427 return wrapper_exit_code;
432 WRITE_MSG(2, "No descriptions supplied, try '");
433 WRITE_STR(2, argv[0]);
434 WRITE_MSG(2, " --help' for more information.\n");
435 return wrapper_exit_code;
439 if (load_description(desc, &head) == -1)
442 for (file = argv + optind; *file; file++) {
443 file_format_t *formats[2];
448 if (!process_file(head, *file, formats) && formats[0]) {
449 WRITE_STR(1, formats[0]->extractor);
450 if (formats[0]->peek_inside) {
452 WRITE_STR(1, formats[1] ? formats[1]->extractor : "unknown format");
456 WRITE_MSG(1, "unknown format");