libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

dvi_extractor.c (8783B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2002, 2003, 2004, 2012, 2017, 2019 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., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19  */
     20 /**
     21  * @file plugins/dvi_extractor.c
     22  * @brief plugin to support DVI files (from LaTeX)
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include "extractor.h"
     27 
     28 
     29 /**
     30  * Pair of a PostScipt prefix and the corresponding LE type.
     31  */
     32 struct Matches
     33 {
     34   /**
     35    * Prefix in the PS map.
     36    */
     37   const char *text;
     38 
     39   /**
     40    * Corresponding LE type.
     41    */
     42   enum EXTRACTOR_MetaType type;
     43 };
     44 
     45 
     46 /**
     47  * Map from PS names to LE types.
     48  */
     49 static struct Matches tmap[] = {
     50   { "/Title (",    EXTRACTOR_METATYPE_TITLE },
     51   { "/Subject (",  EXTRACTOR_METATYPE_SUBJECT },
     52   { "/Author (",   EXTRACTOR_METATYPE_AUTHOR_NAME },
     53   { "/Keywords (", EXTRACTOR_METATYPE_KEYWORDS },
     54   { "/Creator (",  EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE },
     55   { "/Producer (", EXTRACTOR_METATYPE_PRODUCED_BY_SOFTWARE },
     56   { NULL, 0 }
     57 };
     58 
     59 
     60 /**
     61  * Parse a "ZZZ" tag.  Specifically, the data may contain a
     62  * postscript dictionary with metadata.
     63  *
     64  * @param data overall input stream
     65  * @param pos where in data is the zzz data
     66  * @param len how many bytes from 'pos' does the zzz data extend?
     67  * @param proc function to call with meta data found
     68  * @param proc_cls closure for proc
     69  * @return 0 to continue to extract, 1 to stop
     70  */
     71 static int
     72 parseZZZ (const char *data,
     73           size_t pos, size_t len,
     74           EXTRACTOR_MetaDataProcessor proc,
     75           void *proc_cls)
     76 {
     77   size_t slen;
     78   size_t end;
     79   unsigned int i;
     80 
     81   end = pos + len;
     82   slen = strlen ("ps:SDict begin [");
     83   if ( (len <= slen) ||
     84        (0 != strncmp ("ps:SDict begin [ ", &data[pos], slen)) )
     85     return 0;
     86   pos += slen;
     87   while (pos < end)
     88   {
     89     for (i = 0; NULL != tmap[i].text; i++)
     90     {
     91       slen = strlen (tmap[i].text);
     92       if ( (pos + slen > end) ||
     93            (0 != strncmp (&data[pos], tmap[i].text, slen)) )
     94         continue;
     95       pos += slen;
     96       slen = pos;
     97       while ((slen < end) && (data[slen] != ')'))
     98         slen++;
     99       slen = slen - pos;
    100       {
    101         char value[slen + 1];
    102 
    103         value[slen] = '\0';
    104         memcpy (value, &data[pos], slen);
    105         if (0 != proc (proc_cls,
    106                        "dvi",
    107                        tmap[i].type,
    108                        EXTRACTOR_METAFORMAT_C_STRING,
    109                        "text/plain",
    110                        value,
    111                        slen + 1))
    112           return 1;
    113       }
    114       pos += slen + 1;
    115       break;
    116     }
    117     pos++;
    118   }
    119   return 0;
    120 }
    121 
    122 
    123 /**
    124  * Read 32-bit unsigned integer in big-endian format from 'data'.
    125  *
    126  * @param data pointer to integer (possibly unaligned)
    127  * @return 32-bit integer in host byte order
    128  */
    129 static uint32_t
    130 getIntAt (const void *data)
    131 {
    132   uint32_t p;
    133 
    134   memcpy (&p, data, 4);          /* ensure alignment! */
    135   return ntohl (p);
    136 }
    137 
    138 
    139 /**
    140  * Read 16-bit unsigned integer in big-endian format from 'data'.
    141  *
    142  * @param data pointer to integer (possibly unaligned)
    143  * @return 16-bit integer in host byte order
    144  */
    145 static uint16_t
    146 getShortAt (const void *data)
    147 {
    148   uint16_t p;
    149 
    150   memcpy (&p, data, sizeof (uint16_t));          /* ensure alignment! */
    151   return ntohs (p);
    152 }
    153 
    154 
    155 /**
    156  * Main entry method for the 'application/x-dvi' extraction plugin.
    157  *
    158  * @param ec extraction context provided to the plugin
    159  */
    160 void
    161 EXTRACTOR_dvi_extract_method (struct EXTRACTOR_ExtractContext *ec);
    162 
    163 void
    164 EXTRACTOR_dvi_extract_method (struct EXTRACTOR_ExtractContext *ec)
    165 {
    166   unsigned int klen;
    167   uint32_t pos;
    168   uint32_t opos;
    169   unsigned int len;
    170   unsigned int pageCount;
    171   char pages[16];
    172   void *buf;
    173   unsigned char *data;
    174   uint64_t size;
    175   uint64_t off;
    176   ssize_t iret;
    177 
    178   if (40 >= (iret = ec->read (ec->cls, &buf, 1024)))
    179     return;
    180   data = buf;
    181   if ( (data[0] != 247) ||
    182        (data[1] != 2) )
    183     return;                /* cannot be DVI or unsupported version */
    184   klen = data[14];
    185   size = ec->get_size (ec->cls);
    186   if (size > 16 * 1024 * 1024)
    187     return; /* too large */
    188   if (klen + 15 > size)
    189     return; /* malformed klen */
    190   if (NULL == (data = malloc ((size_t) size)))
    191     return; /* out of memory */
    192   memcpy (data, buf, iret);
    193   off = iret;
    194   while (off < size)
    195   {
    196     if (0 >= (iret = ec->read (ec->cls, &buf, 16 * 1024)))
    197     {
    198       free (data);
    199       return;
    200     }
    201     memcpy (&data[off], buf, iret);
    202     off += iret;
    203   }
    204   pos = size - 1;
    205   while ( (223 == data[pos]) &&
    206           (pos > 0) )
    207     pos--;
    208   if ( (2 != data[pos]) ||
    209        (pos < 40) )
    210     goto CLEANUP;
    211   pos--;
    212   pos -= 4;
    213   /* assert pos at 'post_post tag' */
    214   if (data[pos] != 249)
    215     goto CLEANUP;
    216   opos = pos;
    217   pos = getIntAt (&data[opos + 1]);
    218   if ( (pos + 25 > size) ||
    219        (pos + 25 < pos) )
    220     goto CLEANUP;
    221   /* assert pos at 'post' command */
    222   if (data[pos] != 248)
    223     goto CLEANUP;
    224   pageCount = 0;
    225   opos = pos;
    226   pos = getIntAt (&data[opos + 1]);
    227   while (1)
    228   {
    229     if (UINT32_MAX == pos)
    230       break;
    231     if ( (pos + 45 > size) ||
    232          (pos + 45 < pos) )
    233       goto CLEANUP;
    234     if (data[pos] != 139)       /* expect 'bop' */
    235       goto CLEANUP;
    236     pageCount++;
    237     opos = pos;
    238     pos = getIntAt (&data[opos + 41]);
    239     if (UINT32_MAX == pos)
    240       break;
    241     if (pos >= opos)
    242       goto CLEANUP;       /* invalid! */
    243   }
    244   /* ok, now we believe it's a dvi... */
    245   snprintf (pages,
    246             sizeof (pages),
    247             "%u",
    248             pageCount);
    249   if (0 != ec->proc (ec->cls,
    250                      "dvi",
    251                      EXTRACTOR_METATYPE_PAGE_COUNT,
    252                      EXTRACTOR_METAFORMAT_UTF8,
    253                      "text/plain",
    254                      pages,
    255                      strlen (pages) + 1))
    256     goto CLEANUP;
    257   if (0 != ec->proc (ec->cls,
    258                      "dvi",
    259                      EXTRACTOR_METATYPE_MIMETYPE,
    260                      EXTRACTOR_METAFORMAT_UTF8,
    261                      "text/plain",
    262                      "application/x-dvi",
    263                      strlen ("application/x-dvi") + 1))
    264     goto CLEANUP;
    265   {
    266     char comment[klen + 1];
    267 
    268     comment[klen] = '\0';
    269     memcpy (comment, &data[15], klen);
    270     if (0 != ec->proc (ec->cls,
    271                        "dvi",
    272                        EXTRACTOR_METATYPE_COMMENT,
    273                        EXTRACTOR_METAFORMAT_C_STRING,
    274                        "text/plain",
    275                        comment,
    276                        klen + 1))
    277       goto CLEANUP;
    278   }
    279   /* try to find PDF/ps special */
    280   pos = opos;
    281   while ( (size >= 100) &&
    282           (pos < size - 100) )
    283   {
    284     switch (data[pos])
    285     {
    286     case 139:                  /* begin page 'bop', we typically have to skip that one to
    287                                    find the zzz's */
    288       pos += 45;                /* skip bop */
    289       break;
    290     case 239:                  /* zzz1 */
    291       len = data[pos + 1];
    292       if ( (pos + 2 + len < size) &&
    293            (0 != parseZZZ ((const char *) data, pos + 2, len, ec->proc,
    294                            ec->cls)) )
    295         goto CLEANUP;
    296       pos += len + 2;
    297       break;
    298     case 240:                  /* zzz2 */
    299       len = getShortAt (&data[pos + 1]);
    300       if ( (pos + 3 + len < size) &&
    301            (0 != parseZZZ ((const char *) data, pos + 3, len, ec->proc,
    302                            ec->cls)) )
    303         goto CLEANUP;
    304       pos += len + 3;
    305       break;
    306     case 241:                  /* zzz3, who uses that? */
    307       len = (getShortAt (&data[pos + 1])) + 65536 * data[pos + 3];
    308       if ( (pos + 4 + len < size) &&
    309            (0 != parseZZZ ((const char *) data, pos + 4, len, ec->proc,
    310                            ec->cls)) )
    311         goto CLEANUP;
    312       pos += len + 4;
    313       break;
    314     case 242:                  /* zzz4, hurray! */
    315       len = getIntAt (&data[pos + 1]);
    316       if ( (pos + 5 + len < size) &&
    317            (0 != parseZZZ ((const char *) data, pos + 5, len, ec->proc,
    318                            ec->cls)) )
    319         goto CLEANUP;
    320       pos += len + 5;
    321       break;
    322     default:                   /* unsupported opcode, abort scan */
    323       goto CLEANUP;
    324     }
    325   }
    326 CLEANUP:
    327   free (data);
    328 }
    329 
    330 
    331 /* end of dvi_extractor.c */