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 Hubbub.
  3.  * Licensed under the MIT License,
  4.  *                http://www.opensource.org/licenses/mit-license.php
  5.  * Copyright 2007-8 John-Mark Bell <jmb@netsurf-browser.org>
  6.  */
  7.  
  8. #include <assert.h>
  9. #include <string.h>
  10.  
  11. #include <parserutils/charset/mibenum.h>
  12. #include <parserutils/input/inputstream.h>
  13.  
  14. #include <hubbub/parser.h>
  15.  
  16. #include "charset/detect.h"
  17. #include "tokeniser/tokeniser.h"
  18. #include "treebuilder/treebuilder.h"
  19. #include "utils/parserutilserror.h"
  20.  
  21. /**
  22.  * Hubbub parser object
  23.  */
  24. struct hubbub_parser {
  25.         parserutils_inputstream *stream;        /**< Input stream instance */
  26.         hubbub_tokeniser *tok;          /**< Tokeniser instance */
  27.         hubbub_treebuilder *tb;         /**< Treebuilder instance */
  28.  
  29.         hubbub_allocator_fn alloc;      /**< Memory (de)allocation function */
  30.         void *pw;                       /**< Client data */
  31. };
  32.  
  33. /**
  34.  * Create a hubbub parser
  35.  *
  36.  * \param enc      Source document encoding, or NULL to autodetect
  37.  * \param fix_enc  Permit fixing up of encoding if it's frequently misused
  38.  * \param alloc    Memory (de)allocation function
  39.  * \param pw       Pointer to client-specific private data (may be NULL)
  40.  * \param parser   Pointer to location to receive parser instance
  41.  * \return HUBBUB_OK on success,
  42.  *         HUBBUB_BADPARM on bad parameters,
  43.  *         HUBBUB_NOMEM on memory exhaustion,
  44.  *         HUBBUB_BADENCODING if ::enc is unsupported
  45.  */
  46. hubbub_error hubbub_parser_create(const char *enc, bool fix_enc,
  47.                 hubbub_allocator_fn alloc, void *pw, hubbub_parser **parser)
  48. {
  49.         parserutils_error perror;
  50.         hubbub_error error;
  51.         hubbub_parser *p;
  52.  
  53.         if (alloc == NULL || parser == NULL)
  54.                 return HUBBUB_BADPARM;
  55.  
  56.         p = alloc(NULL, sizeof(hubbub_parser), pw);
  57.         if (p == NULL)
  58.                 return HUBBUB_NOMEM;
  59.  
  60.         /* If we have an encoding and we're permitted to fix up likely broken
  61.          * ones, then attempt to do so. */
  62.         if (enc != NULL && fix_enc == true) {
  63.                 uint16_t mibenum = parserutils_charset_mibenum_from_name(enc,
  64.                                 strlen(enc));
  65.  
  66.                 if (mibenum != 0) {
  67.                         hubbub_charset_fix_charset(&mibenum);
  68.  
  69.                         enc = parserutils_charset_mibenum_to_name(mibenum);
  70.                 }
  71.         }
  72.  
  73.         perror = parserutils_inputstream_create(enc,
  74.                 enc != NULL ? HUBBUB_CHARSET_CONFIDENT : HUBBUB_CHARSET_UNKNOWN,
  75.                 hubbub_charset_extract, alloc, pw, &p->stream);
  76.         if (perror != PARSERUTILS_OK) {
  77.                 alloc(p, 0, pw);
  78.                 return hubbub_error_from_parserutils_error(perror);
  79.         }
  80.  
  81.         error = hubbub_tokeniser_create(p->stream, alloc, pw, &p->tok);
  82.         if (error != HUBBUB_OK) {
  83.                 parserutils_inputstream_destroy(p->stream);
  84.                 alloc(p, 0, pw);
  85.                 return error;
  86.         }
  87.  
  88.         error = hubbub_treebuilder_create(p->tok, alloc, pw, &p->tb);
  89.         if (error != HUBBUB_OK) {
  90.                 hubbub_tokeniser_destroy(p->tok);
  91.                 parserutils_inputstream_destroy(p->stream);
  92.                 alloc(p, 0, pw);
  93.                 return error;
  94.         }
  95.  
  96.         p->alloc = alloc;
  97.         p->pw = pw;
  98.  
  99.         *parser = p;
  100.  
  101.         return HUBBUB_OK;
  102. }
  103.  
  104. /**
  105.  * Destroy a hubbub parser
  106.  *
  107.  * \param parser  Parser instance to destroy
  108.  * \return HUBBUB_OK on success, appropriate error otherwise
  109.  */
  110. hubbub_error hubbub_parser_destroy(hubbub_parser *parser)
  111. {
  112.         if (parser == NULL)
  113.                 return HUBBUB_BADPARM;
  114.  
  115.         hubbub_treebuilder_destroy(parser->tb);
  116.  
  117.         hubbub_tokeniser_destroy(parser->tok);
  118.  
  119.         parserutils_inputstream_destroy(parser->stream);
  120.  
  121.         parser->alloc(parser, 0, parser->pw);
  122.  
  123.         return HUBBUB_OK;
  124. }
  125.  
  126. /**
  127.  * Configure a hubbub parser
  128.  *
  129.  * \param parser  Parser instance to configure
  130.  * \param type    Option to set
  131.  * \param params  Option-specific parameters
  132.  * \return HUBBUB_OK on success, appropriate error otherwise
  133.  */
  134. hubbub_error hubbub_parser_setopt(hubbub_parser *parser,
  135.                 hubbub_parser_opttype type,
  136.                 hubbub_parser_optparams *params)
  137. {
  138.         hubbub_error result = HUBBUB_OK;
  139.  
  140.         if (parser == NULL || params == NULL)
  141.                 return HUBBUB_BADPARM;
  142.  
  143.         switch (type) {
  144.         case HUBBUB_PARSER_TOKEN_HANDLER:
  145.                 if (parser->tb != NULL) {
  146.                         /* Client is defining their own token handler,
  147.                          * so we must destroy the default treebuilder */
  148.                         hubbub_treebuilder_destroy(parser->tb);
  149.                         parser->tb = NULL;
  150.                 }
  151.                 result = hubbub_tokeniser_setopt(parser->tok,
  152.                                 HUBBUB_TOKENISER_TOKEN_HANDLER,
  153.                                 (hubbub_tokeniser_optparams *) params);
  154.                 break;
  155.  
  156.         case HUBBUB_PARSER_ERROR_HANDLER:
  157.                 /* The error handler does not cascade, so tell both the
  158.                  * treebuilder (if extant) and the tokeniser. */
  159.                 if (parser->tb != NULL) {
  160.                         result = hubbub_treebuilder_setopt(parser->tb,
  161.                                         HUBBUB_TREEBUILDER_ERROR_HANDLER,
  162.                                         (hubbub_treebuilder_optparams *) params);
  163.                 }
  164.                 if (result == HUBBUB_OK) {
  165.                         result = hubbub_tokeniser_setopt(parser->tok,
  166.                                         HUBBUB_TOKENISER_ERROR_HANDLER,
  167.                                         (hubbub_tokeniser_optparams *) params);
  168.                 }
  169.                 break;
  170.  
  171.         case HUBBUB_PARSER_CONTENT_MODEL:
  172.                 result = hubbub_tokeniser_setopt(parser->tok,
  173.                                 HUBBUB_TOKENISER_CONTENT_MODEL,
  174.                                 (hubbub_tokeniser_optparams *) params);
  175.                 break;
  176.  
  177.         case HUBBUB_PARSER_PAUSE:
  178.                 result = hubbub_tokeniser_setopt(parser->tok,
  179.                                 HUBBUB_TOKENISER_PAUSE,
  180.                                 (hubbub_tokeniser_optparams *) params);
  181.                 break;
  182.  
  183.         case HUBBUB_PARSER_TREE_HANDLER:
  184.                 if (parser->tb != NULL) {
  185.                         result = hubbub_treebuilder_setopt(parser->tb,
  186.                                         HUBBUB_TREEBUILDER_TREE_HANDLER,
  187.                                         (hubbub_treebuilder_optparams *) params);
  188.                 }
  189.                 break;
  190.  
  191.         case HUBBUB_PARSER_DOCUMENT_NODE:
  192.                 if (parser->tb != NULL) {
  193.                         result = hubbub_treebuilder_setopt(parser->tb,
  194.                                         HUBBUB_TREEBUILDER_DOCUMENT_NODE,
  195.                                         (hubbub_treebuilder_optparams *) params);
  196.                 }
  197.                 break;
  198.  
  199.         case HUBBUB_PARSER_ENABLE_SCRIPTING:
  200.                 if (parser->tb != NULL) {
  201.                         result = hubbub_treebuilder_setopt(parser->tb,
  202.                                         HUBBUB_TREEBUILDER_ENABLE_SCRIPTING,
  203.                                         (hubbub_treebuilder_optparams *) params);
  204.                 }
  205.                 break;
  206.  
  207.         default:
  208.                 result = HUBBUB_INVALID;
  209.         }
  210.  
  211.         return result;
  212. }
  213.  
  214. /**
  215.  * Insert a chunk of data into a hubbub parser input stream
  216.  *
  217.  * Inserts the given data into the input stream ready for parsing but
  218.  * does not cause any additional processing of the input. This is
  219.  * useful to allow hubbub callbacks to add computed data to the input.
  220.  *
  221.  * \param parser  Parser instance to use
  222.  * \param data    Data to parse (encoded in UTF-8)
  223.  * \param len     Length, in bytes, of data
  224.  * \return HUBBUB_OK on success, appropriate error otherwise
  225.  */
  226. hubbub_error hubbub_parser_insert_chunk(hubbub_parser *parser,
  227.                 const uint8_t *data, size_t len)
  228. {
  229.         if (parser == NULL || data == NULL)
  230.                 return HUBBUB_BADPARM;
  231.  
  232.         return hubbub_tokeniser_insert_chunk(parser->tok, data, len);
  233. }
  234.  
  235. /**
  236.  * Pass a chunk of data to a hubbub parser for parsing
  237.  *
  238.  * \param parser  Parser instance to use
  239.  * \param data    Data to parse (encoded in the input charset)
  240.  * \param len     Length, in bytes, of data
  241.  * \return HUBBUB_OK on success, appropriate error otherwise
  242.  */
  243. hubbub_error hubbub_parser_parse_chunk(hubbub_parser *parser,
  244.                 const uint8_t *data, size_t len)
  245. {
  246.         parserutils_error perror;
  247.         hubbub_error error;
  248.  
  249.         if (parser == NULL || data == NULL)
  250.                 return HUBBUB_BADPARM;
  251.  
  252.         perror = parserutils_inputstream_append(parser->stream, data, len);
  253.         if (perror != PARSERUTILS_OK)
  254.                 return hubbub_error_from_parserutils_error(perror);
  255.  
  256.         error = hubbub_tokeniser_run(parser->tok);
  257.         if (error == HUBBUB_BADENCODING) {
  258.                 /* Ok, we autodetected an encoding that we don't actually
  259.                  * support. We've not actually processed any data at this
  260.                  * point so fall back to Windows-1252 and hope for the best
  261.                  */
  262.                 perror = parserutils_inputstream_change_charset(parser->stream,
  263.                                 "Windows-1252", HUBBUB_CHARSET_TENTATIVE);
  264.                 /* Under no circumstances should we get here if we've managed
  265.                  * to process data. If there is a way, I want to know about it
  266.                  */
  267.                 assert(perror != PARSERUTILS_INVALID);
  268.                 if (perror != PARSERUTILS_OK)
  269.                         return hubbub_error_from_parserutils_error(perror);
  270.  
  271.                 /* Retry the tokenisation */
  272.                 error = hubbub_tokeniser_run(parser->tok);
  273.         }
  274.  
  275.         if (error != HUBBUB_OK)
  276.                 return error;
  277.  
  278.         return HUBBUB_OK;
  279. }
  280.  
  281. /**
  282.  * Inform the parser that the last chunk of data has been parsed
  283.  *
  284.  * \param parser  Parser to inform
  285.  * \return HUBBUB_OK on success, appropriate error otherwise
  286.  */
  287. hubbub_error hubbub_parser_completed(hubbub_parser *parser)
  288. {
  289.         parserutils_error perror;
  290.         hubbub_error error;
  291.  
  292.         if (parser == NULL)
  293.                 return HUBBUB_BADPARM;
  294.  
  295.         perror = parserutils_inputstream_append(parser->stream, NULL, 0);
  296.         if (perror != PARSERUTILS_OK)
  297.                 return hubbub_error_from_parserutils_error(perror);
  298.  
  299.         error = hubbub_tokeniser_run(parser->tok);
  300.         if (error != HUBBUB_OK)
  301.                 return error;
  302.  
  303.         return HUBBUB_OK;
  304. }
  305.  
  306. /**
  307.  * Read the document charset
  308.  *
  309.  * \param parser  Parser instance to query
  310.  * \param source  Pointer to location to receive charset source
  311.  * \return Pointer to charset name (constant; do not free), or NULL if unknown
  312.  */
  313. const char *hubbub_parser_read_charset(hubbub_parser *parser,
  314.                 hubbub_charset_source *source)
  315. {
  316.         const char *name;
  317.         uint32_t src;
  318.  
  319.         if (parser == NULL || source == NULL)
  320.                 return NULL;
  321.  
  322.         name = parserutils_inputstream_read_charset(parser->stream, &src);
  323.  
  324.         *source = (hubbub_charset_source) src;
  325.  
  326.         return name;
  327. }
  328.  
  329.