Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * This file is part of LibParserUtils.
  3.  * Licensed under the MIT License,
  4.  *                http://www.opensource.org/licenses/mit-license.php
  5.  * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
  6.  */
  7.  
  8. #include <errno.h>
  9. #include <stdbool.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12.  
  13. #define WITHOUT_ICONV_FILTER
  14. #ifndef WITHOUT_ICONV_FILTER
  15. #include <iconv.h>
  16. #endif
  17.  
  18. #include <parserutils/charset/mibenum.h>
  19. #include <parserutils/charset/codec.h>
  20.  
  21. #include "input/filter.h"
  22. #include "utils/utils.h"
  23.  
  24. /** Input filter */
  25. struct parserutils_filter {
  26. #ifndef WITHOUT_ICONV_FILTER
  27.         iconv_t cd;                     /**< Iconv conversion descriptor */
  28.         uint16_t int_enc;               /**< The internal encoding */
  29. #else
  30.         parserutils_charset_codec *read_codec;  /**< Read codec */
  31.         parserutils_charset_codec *write_codec; /**< Write codec */
  32.  
  33.         uint32_t pivot_buf[64];         /**< Conversion pivot buffer */
  34.  
  35.         bool leftover;                  /**< Data remains from last call */
  36.         uint8_t *pivot_left;            /**< Remaining pivot to write */
  37.         size_t pivot_len;               /**< Length of pivot remaining */
  38. #endif
  39.  
  40.         struct {
  41.                 uint16_t encoding;      /**< Input encoding */
  42.         } settings;                     /**< Filter settings */
  43.  
  44.         parserutils_alloc alloc;        /**< Memory (de)allocation function */
  45.         void *pw;                       /**< Client private data */
  46. };
  47.  
  48. static parserutils_error filter_set_defaults(parserutils_filter *input);
  49. static parserutils_error filter_set_encoding(parserutils_filter *input,
  50.                 const char *enc);
  51.  
  52. /**
  53.  * Create an input filter
  54.  *
  55.  * \param int_enc  Desired encoding of document
  56.  * \param alloc    Function used to (de)allocate data
  57.  * \param pw       Pointer to client-specific private data (may be NULL)
  58.  * \param filter   Pointer to location to receive filter instance
  59.  * \return PARSERUTILS_OK on success,
  60.  *         PARSERUTILS_BADPARM on bad parameters,
  61.  *         PARSERUTILS_NOMEM on memory exhausion,
  62.  *         PARSERUTILS_BADENCODING if the encoding is unsupported
  63.  */
  64. parserutils_error parserutils__filter_create(const char *int_enc,
  65.                 parserutils_alloc alloc, void *pw, parserutils_filter **filter)
  66. {
  67.         parserutils_filter *f;
  68.         parserutils_error error;
  69.  
  70.         if (int_enc == NULL || alloc == NULL || filter == NULL)
  71.                 return PARSERUTILS_BADPARM;
  72.  
  73.         f = alloc(NULL, sizeof(parserutils_filter), pw);
  74.         if (f == NULL)
  75.                 return PARSERUTILS_NOMEM;
  76.  
  77. #ifndef WITHOUT_ICONV_FILTER
  78.         f->cd = (iconv_t) -1;
  79.         f->int_enc = parserutils_charset_mibenum_from_name(
  80.                         int_enc, strlen(int_enc));
  81.         if (f->int_enc == 0) {
  82.                 alloc(f, 0, pw);
  83.                 return PARSERUTILS_BADENCODING;
  84.         }
  85. #else
  86.         f->leftover = false;
  87.         f->pivot_left = NULL;
  88.         f->pivot_len = 0;
  89. #endif
  90.  
  91.         f->alloc = alloc;
  92.         f->pw = pw;
  93.  
  94.         error = filter_set_defaults(f);
  95.         if (error != PARSERUTILS_OK) {
  96.                 f->alloc(f, 0, pw);
  97.                 return error;
  98.         }
  99.  
  100. #ifdef WITHOUT_ICONV_FILTER
  101.         error = parserutils_charset_codec_create(int_enc, alloc, pw,
  102.                         &f->write_codec);
  103.         if (error != PARSERUTILS_OK) {
  104.                 if (f->read_codec != NULL) {
  105.                         parserutils_charset_codec_destroy(f->read_codec);
  106.                         f->read_codec = NULL;
  107.                 }
  108.                 f->alloc(f, 0, pw);
  109.                 return error;
  110.         }
  111. #endif
  112.  
  113.         *filter = f;
  114.  
  115.         return PARSERUTILS_OK;
  116. }
  117.  
  118. /**
  119.  * Destroy an input filter
  120.  *
  121.  * \param input  Pointer to filter instance
  122.  * \return PARSERUTILS_OK on success, appropriate error otherwise
  123.  */
  124. parserutils_error parserutils__filter_destroy(parserutils_filter *input)
  125. {
  126.         if (input == NULL)
  127.                 return PARSERUTILS_BADPARM;
  128.  
  129. #ifndef WITHOUT_ICONV_FILTER
  130.         if (input->cd != (iconv_t) -1) {
  131.                 iconv_close(input->cd);
  132.                 input->cd = (iconv_t) -1;
  133.         }
  134. #else
  135.         if (input->read_codec != NULL) {
  136.                 parserutils_charset_codec_destroy(input->read_codec);
  137.                 input->read_codec = NULL;
  138.         }
  139.  
  140.         if (input->write_codec != NULL) {
  141.                 parserutils_charset_codec_destroy(input->write_codec);
  142.                 input->write_codec = NULL;
  143.         }
  144. #endif
  145.  
  146.         input->alloc(input, 0, input->pw);
  147.  
  148.         return PARSERUTILS_OK;
  149. }
  150.  
  151. /**
  152.  * Configure an input filter
  153.  *
  154.  * \param input   Pointer to filter instance
  155.  * \param type    Input option type to configure
  156.  * \param params  Option-specific parameters
  157.  * \return PARSERUTILS_OK on success, appropriate error otherwise
  158.  */
  159. parserutils_error parserutils__filter_setopt(parserutils_filter *input,
  160.                 parserutils_filter_opttype type,
  161.                 parserutils_filter_optparams *params)
  162. {
  163.         parserutils_error error = PARSERUTILS_OK;
  164.  
  165.         if (input == NULL || params == NULL)
  166.                 return PARSERUTILS_BADPARM;
  167.  
  168.         switch (type) {
  169.         case PARSERUTILS_FILTER_SET_ENCODING:
  170.                 error = filter_set_encoding(input, params->encoding.name);
  171.                 break;
  172.         }
  173.  
  174.         return error;
  175. }
  176.  
  177. /**
  178.  * Process a chunk of data
  179.  *
  180.  * \param input   Pointer to filter instance
  181.  * \param data    Pointer to pointer to input buffer
  182.  * \param len     Pointer to length of input buffer
  183.  * \param output  Pointer to pointer to output buffer
  184.  * \param outlen  Pointer to length of output buffer
  185.  * \return PARSERUTILS_OK on success, appropriate error otherwise
  186.  *
  187.  * Call this with an input buffer length of 0 to flush any buffers.
  188.  */
  189. parserutils_error parserutils__filter_process_chunk(parserutils_filter *input,
  190.                 const uint8_t **data, size_t *len,
  191.                 uint8_t **output, size_t *outlen)
  192. {
  193.         if (input == NULL || data == NULL || *data == NULL || len == NULL ||
  194.                         output == NULL || *output == NULL || outlen == NULL)
  195.                 return PARSERUTILS_BADPARM;
  196.  
  197. #ifndef WITHOUT_ICONV_FILTER
  198.         if (iconv(input->cd, (void *) data, len,
  199.                         (char **) output, outlen) == (size_t) -1) {
  200.                 switch (errno) {
  201.                 case E2BIG:
  202.                         return PARSERUTILS_NOMEM;
  203.                 case EILSEQ:
  204.                         if (*outlen < 3)
  205.                                 return PARSERUTILS_NOMEM;
  206.  
  207.                         (*output)[0] = 0xef;
  208.                         (*output)[1] = 0xbf;
  209.                         (*output)[2] = 0xbd;
  210.  
  211.                         *output += 3;
  212.                         *outlen -= 3;
  213.  
  214.                         (*data)++;
  215.                         (*len)--;
  216.  
  217.                         while (*len > 0) {
  218.                                 size_t ret;
  219.                                
  220.                                 ret = iconv(input->cd, (void *) data, len,
  221.                                                 (char **) output, outlen);
  222.                                 if (ret != (size_t) -1 || errno != EILSEQ)
  223.                                         break;
  224.  
  225.                                 if (*outlen < 3)
  226.                                         return PARSERUTILS_NOMEM;
  227.  
  228.                                 (*output)[0] = 0xef;
  229.                                 (*output)[1] = 0xbf;
  230.                                 (*output)[2] = 0xbd;
  231.  
  232.                                 *output += 3;
  233.                                 *outlen -= 3;
  234.  
  235.                                 (*data)++;
  236.                                 (*len)--;
  237.                         }
  238.  
  239.                         return errno == E2BIG ? PARSERUTILS_NOMEM
  240.                                               : PARSERUTILS_OK;
  241.                 }
  242.         }
  243.  
  244.         return PARSERUTILS_OK;
  245. #else
  246.         if (input->leftover) {
  247.                 parserutils_error write_error;
  248.  
  249.                 /* Some data left to be written from last call */
  250.  
  251.                 /* Attempt to flush the remaining data. */
  252.                 write_error = parserutils_charset_codec_encode(
  253.                                 input->write_codec,
  254.                                 (const uint8_t **) &input->pivot_left,
  255.                                 &input->pivot_len,
  256.                                 output, outlen);
  257.  
  258.                 if (write_error != PARSERUTILS_OK)
  259.                         return write_error;
  260.  
  261.  
  262.                 /* And clear leftover */
  263.                 input->pivot_left = NULL;
  264.                 input->pivot_len = 0;
  265.                 input->leftover = false;
  266.         }
  267.  
  268.         while (*len > 0) {
  269.                 parserutils_error read_error, write_error;
  270.                 size_t pivot_len = sizeof(input->pivot_buf);
  271.                 uint8_t *pivot = (uint8_t *) input->pivot_buf;
  272.  
  273.                 read_error = parserutils_charset_codec_decode(input->read_codec,
  274.                                 data, len,
  275.                                 (uint8_t **) &pivot, &pivot_len);
  276.  
  277.                 pivot = (uint8_t *) input->pivot_buf;
  278.                 pivot_len = sizeof(input->pivot_buf) - pivot_len;
  279.  
  280.                 if (pivot_len > 0) {
  281.                         write_error = parserutils_charset_codec_encode(
  282.                                         input->write_codec,
  283.                                         (const uint8_t **) &pivot,
  284.                                         &pivot_len,
  285.                                         output, outlen);
  286.  
  287.                         if (write_error != PARSERUTILS_OK) {
  288.                                 input->leftover = true;
  289.                                 input->pivot_left = pivot;
  290.                                 input->pivot_len = pivot_len;
  291.  
  292.                                 return write_error;
  293.                         }
  294.                 }
  295.  
  296.                 if (read_error != PARSERUTILS_OK &&
  297.                                 read_error != PARSERUTILS_NOMEM)
  298.                         return read_error;
  299.         }
  300.  
  301.         return PARSERUTILS_OK;
  302. #endif
  303. }
  304.  
  305. /**
  306.  * Reset an input filter's state
  307.  *
  308.  * \param input  The input filter to reset
  309.  * \return PARSERUTILS_OK on success, appropriate error otherwise
  310.  */
  311. parserutils_error parserutils__filter_reset(parserutils_filter *input)
  312. {
  313.         parserutils_error error = PARSERUTILS_OK;
  314.  
  315.         if (input == NULL)
  316.                 return PARSERUTILS_BADPARM;
  317.  
  318. #ifndef WITHOUT_ICONV_FILTER
  319.         iconv(input->cd, NULL, 0, NULL, 0);
  320. #else
  321.         /* Clear pivot buffer leftovers */
  322.         input->pivot_left = NULL;
  323.         input->pivot_len = 0;
  324.         input->leftover = false;
  325.  
  326.         /* Reset read codec */
  327.         error = parserutils_charset_codec_reset(input->read_codec);
  328.         if (error != PARSERUTILS_OK)
  329.                 return error;
  330.  
  331.         /* Reset write codec */
  332.         error = parserutils_charset_codec_reset(input->write_codec);
  333.         if (error != PARSERUTILS_OK)
  334.                 return error;
  335. #endif
  336.  
  337.         return error;
  338. }
  339.  
  340. /**
  341.  * Set an input filter's default settings
  342.  *
  343.  * \param input  Input filter to configure
  344.  * \return PARSERUTILS_OK on success, appropriate error otherwise
  345.  */
  346. parserutils_error filter_set_defaults(parserutils_filter *input)
  347. {
  348.         parserutils_error error;
  349.  
  350.         if (input == NULL)
  351.                 return PARSERUTILS_BADPARM;
  352.  
  353. #ifdef WITHOUT_ICONV_FILTER
  354.         input->read_codec = NULL;
  355.         input->write_codec = NULL;
  356. #endif
  357.  
  358.         input->settings.encoding = 0;
  359.         error = filter_set_encoding(input, "UTF-8");
  360.         if (error != PARSERUTILS_OK)
  361.                 return error;
  362.  
  363.         return PARSERUTILS_OK;
  364. }
  365.  
  366. /**
  367.  * Set an input filter's encoding
  368.  *
  369.  * \param input  Input filter to configure
  370.  * \param enc    Encoding name
  371.  * \return PARSERUTILS_OK on success, appropriate error otherwise
  372.  */
  373. parserutils_error filter_set_encoding(parserutils_filter *input,
  374.                 const char *enc)
  375. {
  376.         parserutils_error error = PARSERUTILS_OK;
  377.         uint16_t mibenum;
  378.  
  379.         if (input == NULL || enc == NULL)
  380.                 return PARSERUTILS_BADPARM;
  381.  
  382.         mibenum = parserutils_charset_mibenum_from_name(enc, strlen(enc));
  383.         if (mibenum == 0)
  384.                 return PARSERUTILS_BADENCODING;
  385.  
  386.         /* Exit early if we're already using this encoding */
  387.         if (input->settings.encoding == mibenum)
  388.                 return PARSERUTILS_OK;
  389.  
  390. #ifndef WITHOUT_ICONV_FILTER
  391.         if (input->cd != (iconv_t) -1) {
  392.                 iconv_close(input->cd);
  393.                 input->cd = (iconv_t) -1;
  394.         }
  395.  
  396.         input->cd = iconv_open(
  397.                 parserutils_charset_mibenum_to_name(input->int_enc),
  398.                 parserutils_charset_mibenum_to_name(mibenum));
  399.         if (input->cd == (iconv_t) -1) {
  400.                 return (errno == EINVAL) ? PARSERUTILS_BADENCODING
  401.                                          : PARSERUTILS_NOMEM;
  402.         }
  403. #else
  404.         if (input->read_codec != NULL) {
  405.                 parserutils_charset_codec_destroy(input->read_codec);
  406.                 input->read_codec = NULL;
  407.         }
  408.  
  409.         error = parserutils_charset_codec_create(enc, input->alloc,
  410.                         input->pw, &input->read_codec);
  411.         if (error != PARSERUTILS_OK)
  412.                 return error;
  413. #endif
  414.  
  415.         input->settings.encoding = mibenum;
  416.  
  417.         return error;
  418.  
  419. }
  420.