Replace file with h2ext for the template build method.
authorDaniel Hokka Zakrisson <daniel@hozac.com>
Tue, 10 Jul 2007 20:34:48 +0000 (20:34 +0000)
committerDaniel Hokka Zakrisson <daniel@hozac.com>
Tue, 10 Jul 2007 20:34:48 +0000 (20:34 +0000)
git-svn-id: http://svn.linux-vserver.org/svn/util-vserver/trunk@2558 94cd875c-1c1d-0410-91d2-eb244daf1a30

contrib/manifest.dat.pathsubst
distrib/Makefile.am
distrib/misc/h2ext.desc [new file with mode: 0644]
pathconfig.h.pathsubst
scripts/Makefile-files
scripts/h2ext-worker [new file with mode: 0644]
scripts/magic.mime [deleted file]
scripts/util-vserver-vars.pathsubst
scripts/vserver-build.template
src/Makefile-files
src/h2ext.c [new file with mode: 0644]

index ccf9291..08eca55 100644 (file)
@@ -17,7 +17,6 @@ build  @PKGLIBDIR@/vserver-build.functions.rpm
 build  @PKGLIBDIR@/vserver-build.functions.yum
 build  @PKGLIBDIR@/vserver-build.functions.pkgmgmt
 build  @PKGLIBDIR@/vserver-setup.functions
-build  @PKGLIBDIR@/magic.mime
 base   @PKGLIBDIR@/vserver.functions
 base   @PKGLIBDIR@/vserver.start
 @ENSC_HAVE_C99_COMPILER_TRUE@base   @PKGLIBDIR@/vserver.start.bin
@@ -48,6 +47,7 @@ build  @PKGLIBDIR@/defaults/debootstrap.uri
 build  @PKGLIBDIR@/defaults/context.start
 build  @PKGLIBDIR@/defaults/debootstrap.mirror
 @ENSC_HAVE_C99_COMPILER_TRUE@build  @PKGLIBDIR@/defaults/vunify-exclude
+build  @PKGLIBDIR@/defaults/h2ext.desc
 base   @PKGLIBDIR@/capchroot
 base   @PKGLIBDIR@/chain-echo
 core   @PKGLIBDIR@/chcontext-compat
@@ -57,6 +57,8 @@ base   @PKGLIBDIR@/chroot-sh
 base   @PKGLIBDIR@/exec-ulimit
 base   @PKGLIBDIR@/fakerunlevel
 base   @PKGLIBDIR@/filetime
+build  @PKGLIBDIR@/h2ext
+build  @PKGLIBDIR@/h2ext-worker
 base   @PKGLIBDIR@/lockfile
 base   @PKGLIBDIR@/keep-ctx-alive
 base   @PKGLIBDIR@/mask2prefix
index 487116b..1b254da 100644 (file)
@@ -28,7 +28,8 @@ defaults_DATA =               misc/debootstrap.mirror \
                        misc/vprocunhide-files \
                        misc/vunify-exclude \
                        misc/environment \
-                       misc/context.start
+                       misc/context.start \
+                       misc/h2ext.desc
 
 nobase_distrib_SCRIPTS =  redhat/initpost \
                           redhat/initpre \
diff --git a/distrib/misc/h2ext.desc b/distrib/misc/h2ext.desc
new file mode 100644 (file)
index 0000000..01afd3a
--- /dev/null
@@ -0,0 +1,19 @@
+# Borrowed from file(1)
+# gzip (GNU zip, not to be confused with [Info-ZIP/PKWARE] zip archiver)
+0      string          \037\213                gzip -d -c      1
+# tar posix
+257    string          ustar\0                 tar -x          0
+# tar gnu
+257    string          ustar\040\040\0         tar -x          0
+# cpio
+0      short           070707                  cpio -i         0
+# cpio swapped
+0      short           0143561                 cpio -i         0
+# bzip2
+0      string          BZh                     bzip2 -d -c     1
+# dump/restore new-fs big and little endian
+24     belong          60012                   restore -rf -   0
+24     lelong          60012                   restore -rf -   0
+# dump/restore old-fs big and little endian
+24     belong          60011                   restore -rf -   0
+24     lelong          60011                   restore -rf -   0
index 941af01..4679e02 100644 (file)
@@ -44,6 +44,7 @@
 #define PROG_VCONFIG           "@VCONFIG@"
 
 #define PROG_SECURE_MOUNT      PKGLIBDIR "/secure-mount"
+#define PROG_H2EXT_WORKER      PKGLIBDIR "/h2ext-worker"
 
 #define PATH_VSERVER_START     PKGLIBDIR "/vserver.start"
 #define PATH_FUNCTIONS         PKGLIBDIR "/functions"
index 128c5a4..c59390a 100644 (file)
@@ -38,7 +38,6 @@ AM_INSTALLCHECK_STD_OPTIONS_EXEMPT += \
 
 
 scripts_pkglib_src_DTA =       scripts/functions \
-                               scripts/magic.mime \
                                scripts/vserver-build.apt-rpm \
                                scripts/vserver-build.skeleton \
                                scripts/vserver-build.debootstrap \
@@ -75,7 +74,8 @@ scripts_pkglib_src_SCRPTS =   scripts/pkgmgmt \
                                scripts/vservers.grabinfo.sh \
                                scripts/vshelper \
                                scripts/vsysvwrapper \
-                               scripts/vyum-worker
+                               scripts/vyum-worker \
+                               scripts/h2ext-worker
 
 scripts_legacy_src_SCRPTS =    scripts/legacy/save_s_context \
                                scripts/legacy/vserver \
diff --git a/scripts/h2ext-worker b/scripts/h2ext-worker
new file mode 100644 (file)
index 0000000..9f55675
--- /dev/null
@@ -0,0 +1,29 @@
+#! /bin/bash
+# $Id$
+
+# Copyright (C) 2007 Daniel Hokka Zakrisson
+#  
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#  
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#  
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# set -e
+
+: ${UTIL_VSERVER_VARS:=/usr/lib/util-vserver/util-vserver-vars}
+test -e "$UTIL_VSERVER_VARS" || {
+    echo $"Can not find util-vserver installation (the file '$UTIL_VSERVER_VARS' would be expected); aborting..." >&2
+    exit 1
+}
+. "$UTIL_VSERVER_VARS"
+
+eval command=( $1 )
+exec "${command[@]}"
diff --git a/scripts/magic.mime b/scripts/magic.mime
deleted file mode 100644 (file)
index 0e10d84..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-# Borrowed from file(1)
-# gzip (GNU zip, not to be confused with [Info-ZIP/PKWARE] zip archiver)
-0      string          \037\213                application/x-gzip
-# tar posix
-257    string          ustar\0                 application/x-tar
-# tar gnu
-257    string          ustar\040\040\0         application/x-tar
-# cpio
-0      short           070707                  application/x-cpio
-# cpio swapped
-0      short           0143561                 application/x-cpio
-# bzip2
-0      string          BZh                     application/x-bzip2
-# dump/restore new-fs big and little endian
-24     belong          60012                   application/x-dump
-24     lelong          60012                   application/x-dump
-# dump/restore old-fs big and little endian
-24     belong          60011                   application/x-dump
-24     lelong          60011                   application/x-dump
index 7b4b450..4a87f34 100644 (file)
@@ -40,6 +40,7 @@ _EXEC_CD="$__SBINDIR/exec-cd"
 _EXEC_ULIMIT="$__PKGLIBDIR/exec-ulimit"
 _FAKE_RUNLEVEL="$__PKGLIBDIR/fakerunlevel"
 _FILETIME="$__PKGLIBDIR/filetime"
+_H2EXT="$__PKGLIBDIR/h2ext"
 _IFSPEC="$__LEGACYDIR/ifspec"
 _INITSYNC_MINIT_START=:
 _KEEP_CTX_ALIVE="$__PKGLIBDIR/keep-ctx-alive"
index 27616cb..77b7326 100644 (file)
@@ -69,30 +69,15 @@ test -z "$BUILD_INITPRE"  || "$BUILD_INITPRE" "$SETUP_CONFDIR"  "$UTIL_VSERVER_V
 
 pushd "$SETUP_CONFDIR/vdir" &> /dev/null
 for t in "${TEMPLATE[@]}"; do
-    mime=$($_FILE -Nbiz -m "$_LIB_MAGIC" "$t")
-    compression=$(echo "$mime" | $_GREP '(' | $_SED 's!.*(\(.*\))$!\1!')
-
-    case "$compression" in
-       application/x-bzip2)    DECOMPRESS="$_BZIP2 -d -c";;
-       application/x-gzip)     DECOMPRESS="$_GZIP -d -c";;
-       "")                     DECOMPRESS=$_CAT;;
-       *)
-           echo "Unsupported compression method: $compression"
-           exit 1
-           ;;
-    esac
+    findFile DESC "$__CONFDIR/.defaults/apps/h2ext/desc" "$__PKGLIBDEFAULTDIR/h2ext.desc"
+    pipe=$($_H2EXT -d "$DESC" -q "$t")
 
-    case "$mime" in
-       application/x-tar*)     EXTRACT="$_TAR -x";;
-       application/x-cpio*)    EXTRACT="$_CPIO -i";;
-       application/x-dump*)    EXTRACT="$_RESTORE -rf -";;
-       *)
-           echo "Unsupported packaging method: $mime"
-           exit 1
-           ;;
-    esac
+    if echo "$pipe" | grep -q "unknown format"; then
+       echo "vserver-build.template: $t: Unknown format" >&2
+       exit 1
+    fi
 
-    $DECOMPRESS "$t" | $EXTRACT
+    eval `echo $_CAT "\$t" '|' $pipe`
 done
 
 test -z "$no_dev_sanity" && {
index 370d569..dad36c1 100644 (file)
@@ -80,7 +80,8 @@ DIETPROGS +=          src/chcontext-compat \
                        src/naddress \
                        src/vdevmap \
                        src/vsysctl \
-                       src/vclone
+                       src/vclone \
+                       src/h2ext
 
 if ENSC_CAN_BEECRYPT_WITH_DIETLIBC
 DIETPROGS +=           src/vhashify
@@ -118,6 +119,7 @@ pkglib_PROGRAMS +=  src/capchroot \
                        src/sigexec \
                        src/chbind-compat \
                        src/vsysctl \
+                       src/h2ext \
                        $(src_pkglib_C99_X_PROGS) \
                        $(src_pkglib_CXX_X_PROGS)
 
@@ -336,6 +338,10 @@ src_vclone_SOURCES         = src/vclone.c
 src_vclone_LDADD               = $(LIBINTERNAL) $(VSERVER_LDADDS)
 src_vclone_LDFLAGS             = $(VSERVER_LDLFGS)
 
+src_h2ext_SOURCES              = src/h2ext.c
+src_h2ext_LDADD                        = $(LIBINTERNAL) $(VSERVER_LDADDS)
+src_h2ext_LDFLAGS              = $(VSERVER_LDFLGS)
+
 EXTRA_PROGRAMS +=              $(src_sbin_CXX_PROGS) $(src_pkglib_CXX_PROGS)
 
 TESTS_ENVIRONMENT +=           srctestsuitedir=$(top_builddir)/src/testsuite
diff --git a/src/h2ext.c b/src/h2ext.c
new file mode 100644 (file)
index 0000000..d1050c8
--- /dev/null
@@ -0,0 +1,439 @@
+// $Id$
+
+// Copyright (C) 2007 Daniel Hokka Zakrisson <daniel@hozac.com>
+//  
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2, or (at your option)
+// any later version.
+//  
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//  
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/wait.h>
+
+#include "util.h"
+#include "lib/internal.h"
+#include "pathconfig.h"
+
+#define ENSC_WRAPPERS_PREFIX "h2ext: "
+#define ENSC_WRAPPERS_UNISTD 1
+#define ENSC_WRAPPERS_FCNTL  1
+#define ENSC_WRAPPERS_STAT   1
+#include <wrappers.h>
+
+#define MAX_PEEK_SIZE 4096
+#define MIN(a,b)  (((a) > (b)) ? (b) : (a))
+
+struct file_format {
+  /* where the value would be in the file */
+  long         offset;
+  /* type of match */
+  enum {
+    FFT_STRING = 1,
+    FFT_SHORT,
+    FFT_LONG,
+    FFT_LE = 0x4000,
+    FFT_BE = 0x8000,
+  }            type;
+  /* the value */
+  union {
+    char *     st;
+    uint16_t   sh;
+    uint32_t   lo;
+  }            value;
+  /* length of the value */
+  size_t       len;
+  /* program to use for extraction */
+  char *       extractor;
+  /* should we try to process the contents as well? */
+  int          peek_inside;
+
+  struct file_format *next;
+};
+typedef struct file_format file_format_t;
+
+int wrapper_exit_code = 255;
+
+#define CMD_HELP               0x4001
+#define CMD_VERSION            0x4002
+
+struct option const
+CMDLINE_OPTIONS[] = {
+  { "help",       no_argument,       0, CMD_HELP },
+  { "version",    no_argument,       0, CMD_VERSION },
+  { "desc",       required_argument, 0, 'd' },
+  { "silent",     no_argument,       0, 'q' },
+  { 0,0,0,0 },
+};
+
+static void
+showHelp(int fd, char const *cmd, int res)
+{
+  WRITE_MSG(fd, "Usage:\n  ");
+  WRITE_STR(fd, cmd);
+  WRITE_MSG(fd,
+           " -d <descriptions file> <file1> [<file2>...]\n\n"
+           "Please report bugs to " PACKAGE_BUGREPORT "\n");
+
+  exit(res);
+}
+
+static void
+showVersion()
+{
+  WRITE_MSG(1,
+           "h2ext " VERSION " -- determines how to extract a file\n"
+           "This program is part of " PACKAGE_STRING "\n\n"
+           "Copyright (C) 2007 Daniel Hokka Zakrisson\n"
+           VERSION_COPYRIGHT_DISCLAIMER);
+  exit(0);
+}
+
+static file_format_t *
+find_format(file_format_t *head, char *data)
+{
+  file_format_t *i;
+
+  for (i = head; i; i = i->next) {
+    switch (i->type & ~(FFT_LE|FFT_BE)) {
+    case FFT_STRING:
+      if (memcmp(i->value.st, data + i->offset, i->len) == 0)
+       goto found;
+      break;
+    case FFT_SHORT:
+      if (i->value.sh == *((__typeof__(i->value.sh) *)data + i->offset))
+       goto found;
+      break;
+    case FFT_LONG:
+      if (i->value.lo == *((__typeof__(i->value.lo) *)data + i->offset))
+       goto found;
+      break;
+    }
+  }
+found:
+  return i;
+}
+
+static int
+process_file(file_format_t *head, const char *file, file_format_t *ret[2])
+{
+  int fd;
+  void *mapping;
+  struct stat st;
+
+  fd = EopenD(file, O_RDONLY, 0);
+  Efstat(fd, &st);
+  mapping = mmap(NULL, MIN(st.st_size, MAX_PEEK_SIZE), PROT_READ, MAP_SHARED, fd, 0);
+  if (!mapping) {
+    perror("mmap()");
+    Eclose(fd);
+    return -1;
+  }
+
+  ret[0] = find_format(head, mapping);
+
+  munmap(mapping, MIN(st.st_size, MAX_PEEK_SIZE));
+
+  if (ret[0] && ret[0]->peek_inside) {
+    pid_t child;
+    int   fds[2];
+
+    Elseek(fd, 0, SEEK_SET);
+
+    Epipe(fds);
+    child = Efork();
+    if (child == 0) {
+      char *argv[3] = { PROG_H2EXT_WORKER, ret[0]->extractor, NULL };
+      dup2(fd, 0);
+      dup2(fds[1], 1);
+      EexecvpD(PROG_H2EXT_WORKER, argv);
+    }
+    else {
+      char *buf = calloc(MAX_PEEK_SIZE, sizeof(char)), *cur, *end;
+      ssize_t bytes_read;
+
+      /* read MAX_PEEK_SIZE bytes from the decompressor */
+      cur = buf;
+      end = buf + MAX_PEEK_SIZE;
+      while (cur < end && (bytes_read = Eread(fds[0], cur, end - cur - 1)) > 0)
+       cur += bytes_read;
+
+      /* get rid of the child */
+      kill(child, SIGTERM);
+      wait(NULL);
+
+      ret[1] = find_format(head, buf);
+      free(buf);
+    }
+  }
+  else
+    ret[1] = NULL;
+
+  Eclose(fd);
+
+  return 0;
+}
+
+static inline void
+byteswap(void *p, size_t len)
+{
+  size_t i;
+  char *buf = p, tmp;
+  for (i = 0; i < (len >> 1); i++) {
+    tmp = buf[len - i - 1];
+    buf[len - i - 1] = buf[i];
+    buf[i] = tmp;
+  }
+}
+
+static inline ALWAYSINLINE void
+WRITE_INT(int fd, int num)
+{
+  char   buf[sizeof(num)*3+2];
+  size_t l;
+
+  l = utilvserver_fmt_long(buf,num);
+
+  Vwrite(fd, buf, l);
+}
+
+static int
+load_description(const char *file, file_format_t **head)
+{
+  file_format_t *prev = NULL,
+               *i = NULL;
+  int          fd,
+               line_no = 0;
+  char         buf[512],
+               *field,
+               *end = buf,
+               *ptr,
+               *eol;
+  ssize_t      bytes_read;
+
+  fd = EopenD(file, O_RDONLY, 0);
+
+  *buf = '\0';
+  while (1) {
+    if ((eol = strchr(buf, '\n')) == NULL && (end - buf) < (sizeof(buf) - 1)) {
+      bytes_read = Eread(fd, end, sizeof(buf) - 1 - (end - buf));
+      /* EOF, implicit newline */
+      if (bytes_read == 0) {
+       if (end == buf)
+         break;
+       eol = end;
+       *(end++) = '\n';
+      }
+      end += bytes_read;
+      *end = '\0';
+      continue;
+    }
+    else if (eol == NULL) {
+      WRITE_MSG(2, ENSC_WRAPPERS_PREFIX);
+      WRITE_STR(2, file);
+      WRITE_MSG(2, ":");
+      WRITE_INT(2, line_no);
+      WRITE_MSG(2, " is a really long line\n");
+      Eclose(fd);
+      return -1;
+    }
+    *eol = '\0';
+    line_no++;
+
+    if (*buf == '#' || *buf == '\0')
+      goto new_line;
+    if (*head == NULL)
+      i = *head = calloc(1, sizeof(file_format_t));
+    else {
+      i->next = calloc(1, sizeof(file_format_t));
+      prev = i;
+      i = i->next;
+    }
+    i->next = NULL;
+
+#define get_field()  for (ptr++; *ptr == '\t' && *ptr != '\0'; ptr++); \
+      for (field = ptr; *ptr != '\t' && *ptr != '\0'; ptr++); \
+      *ptr = '\0';
+    field = ptr = buf;
+    while (*ptr != '\t' && *ptr != '\0')
+      ptr++;
+    *ptr = '\0';
+    if (field == ptr)
+      goto new_line_and_free;
+    i->offset = strtol(field, NULL, 0);
+
+    get_field();
+    if (strcmp(field, "string") == 0)
+      i->type = FFT_STRING;
+    else if (strcmp(field, "short") == 0)
+      i->type = FFT_SHORT;
+    else if (strcmp(field, "long") == 0)
+      i->type = FFT_LONG;
+    else if (strcmp(field, "leshort") == 0)
+      i->type = FFT_SHORT|FFT_LE;
+    else if (strcmp(field, "beshort") == 0)
+      i->type = FFT_SHORT|FFT_BE;
+    else if (strcmp(field, "lelong") == 0)
+      i->type = FFT_LONG|FFT_LE;
+    else if (strcmp(field, "belong") == 0)
+      i->type = FFT_LONG|FFT_BE;
+    else {
+      WRITE_MSG(2, ENSC_WRAPPERS_PREFIX);
+      WRITE_STR(2, file);
+      WRITE_MSG(2, ":");
+      WRITE_INT(2, line_no);
+      WRITE_MSG(2, " has an unknown type: ");
+      WRITE_STR(2, field);
+      WRITE_MSG(2, "\n");
+      goto new_line_and_free;
+    }
+
+    get_field();
+    switch (i->type & ~(FFT_BE|FFT_LE)) {
+    case FFT_STRING:
+      {
+      char *c, *tmp;
+      i->value.st = tmp = calloc(strlen(field) + 1, sizeof(char));
+      for (c = field; *c; c++) {
+       if (*c == '\\') {
+         char *endptr;
+         *(tmp++) = (char)strtol(c + 1, &endptr, 8);
+         c = endptr - 1;
+       }
+       else
+         *(tmp++) = *c;
+      }
+      *tmp = '\0';
+      i->len = tmp - i->value.st;
+      }
+      break;
+    case FFT_SHORT:
+      i->len = sizeof(i->value.sh);
+      i->value.sh = (__typeof__(i->value.sh))strtol(field, NULL, 0);
+#if BYTE_ORDER != BIG_ENDIAN
+      if (i->type & FFT_BE)
+#elif BYTE_ORDER != LITTLE_ENDIAN
+      if (i->type & FFT_LE)
+#else
+#  error UNKNOWN BYTE ORDER
+#endif
+       byteswap(&i->value.sh, i->len);
+      break;
+    case FFT_LONG:
+      i->len = sizeof(i->value.lo);
+      i->value.lo = (__typeof__(i->value.lo))strtol(field, NULL, 0);
+#if BYTE_ORDER != BIG_ENDIAN
+      if (i->type & FFT_BE)
+#elif BYTE_ORDER != LITTLE_ENDIAN
+      if (i->type & FFT_LE)
+#else
+#  error UNKNOWN BYTE ORDER
+#endif
+       byteswap(&i->value.lo, i->len);
+      break;
+    }
+
+    get_field();
+    i->extractor = strdup(field);
+
+    get_field();
+    i->peek_inside = (int)strtol(field, NULL, 0);
+
+#undef get_field
+    goto new_line;
+
+new_line_and_free:
+    free(i);
+    if (prev) {
+      i = prev;
+      free(i->next);
+      i->next = NULL;
+    }
+    else
+      *head = i = NULL;
+new_line:
+    memmove(buf, eol + 1, end - (eol + 1));
+    end = buf + (end - (eol + 1));
+  }
+
+  Eclose(fd);
+  return 0;
+}
+
+int main(int argc, char *argv[])
+{
+  char         **file = NULL,
+               *desc = NULL;
+  file_format_t        *head = NULL;
+  int          quiet = 0;
+
+  while (1) {
+    int c = getopt_long(argc, argv, "+d:q", CMDLINE_OPTIONS, 0);
+    if (c == -1) break;
+
+    switch (c) {
+    case CMD_HELP:     showHelp(1, argv[0], 0);
+    case CMD_VERSION:  showVersion();
+    case 'd':          desc = optarg; break;
+    case 'q':          quiet = 1;     break;
+    default:
+      WRITE_MSG(2, "Try '");
+      WRITE_STR(2, argv[0]);
+      WRITE_MSG(2, " --help' for more information.\n");
+      return wrapper_exit_code;
+    }
+  }
+
+  if (desc == NULL) {
+    WRITE_MSG(2, "No descriptions supplied, try '");
+    WRITE_STR(2, argv[0]);
+    WRITE_MSG(2, " --help' for more information.\n");
+    return wrapper_exit_code;
+  }
+
+  head = NULL;
+  if (load_description(desc, &head) == -1)
+    return EXIT_FAILURE;
+
+  for (file = argv + optind; *file; file++) {
+    file_format_t *formats[2];
+    if (!quiet) {
+      WRITE_STR(1, *file);
+      WRITE_MSG(1, ": ");
+    }
+    if (!process_file(head, *file, formats) && formats[0]) {
+      WRITE_STR(1, formats[0]->extractor);
+      if (formats[0]->peek_inside) {
+       WRITE_MSG(1, " | ");
+       WRITE_STR(1, formats[1] ? formats[1]->extractor : "unknown format");
+      }
+    }
+    else
+      WRITE_MSG(1, "unknown format");
+    WRITE_MSG(1, "\n");
+  }
+
+  return 0;
+}