Incomplete major refactoring

This commit is contained in:
Wilson Lin 2019-01-25 21:25:10 +13:00
parent 277ea3303c
commit 4b98c6a6b2
88 changed files with 4468 additions and 4819 deletions

24
.clang-format Normal file
View File

@ -0,0 +1,24 @@
# Linux kernel style
BasedOnStyle: LLVM
IndentWidth: 8
UseTab: Always
AlignAfterOpenBracket: true
AlignEscapedNewlinesLeft: false
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakBeforeMultilineStrings: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Linux
BreakStringLiterals: false
ContinuationIndentWidth: 8
IndentCaseLabels: false
MaxEmptyLinesToKeep: 2
SortIncludes: false
# Custom
DerivePointerAlignment: false
PointerAlignment: Left
SpaceAfterCStyleCast: true

@ -1 +1 @@
Subproject commit da9f3a02644f7bd7fc24ad29832c289460854eae
Subproject commit 71152e7ed6e4b1e59d1eac7f583d8e3830632973

View File

@ -1 +1 @@
../ext/nicehash/src/main/c
../ext/nicehash/src

View File

@ -1,8 +0,0 @@
# Double underscore files
## __main__.c
- Provide initialise function
- Include all sources for easy usage (single usage)
## __base__.c

View File

@ -0,0 +1,22 @@
# Scope naming
## Public
```c
int hb_sub_function_name(int a, int b);
```
## Internal use only
Used across multiple files but should only be used by this project's code.
```c
int _hb_sub_function_name(int a, int b);
```
## Within same file only
```c
// Don't declare in header file
static int _function_name(int a, int b) {}
```

View File

@ -1,4 +0,0 @@
void hb_init(void) {
// Set up rules
hbr_init();
}

142
src/cli.c
View File

@ -1,142 +0,0 @@
#include <getopt.h>
#include "./stream/content/html.c"
#include "./__main__.c"
int main(int argc, char **argv) {
hb_init();
hbe_err_t err = NULL;
hbe_err_t *hbe_err = &err;
hbu_fstreamout_t output = NULL;
hbu_list_char_t output_buffer = NULL;
// Prepare config
char *input_path = NULL;
char *output_path = NULL;
int logging = 0;
int config_keep = 0;
int config_buffer = 0;
hbu_streamoptions_t config_stream = hbu_streamoptions_create();
int nondefault_ex_collapse_whitespace = 0;
int nondefault_ex_destroy_whole_whitespace = 0;
int nondefault_ex_trim_whitespace = 0;
// Parse arguments
while (1) {
struct option long_options[] = {
{"keep", no_argument, &config_keep, 1},
{"buffer", no_argument, &config_buffer, 1},
{"verbose", no_argument, &logging, 1},
{"input", required_argument, NULL, 'i'},
{"output", required_argument, NULL, 'o'},
{"suppress", required_argument, NULL, 's'},
{"MXcollapseWhitespace", optional_argument, NULL, 40},
{"MXdestroyWholeWhitespace", optional_argument, NULL, 41},
{"MXtrimWhitespace", optional_argument, NULL, 42},
{"MXtrimClassAttr", no_argument, &(config_stream->trim_class_attr), 0},
{"MXdecEnt", no_argument, &(config_stream->decode_entities), 0},
{"MXcondComments", no_argument, &(config_stream->min_conditional_comments), 0},
{"MXattrQuotes", no_argument, &(config_stream->decode_entities), 0},
{"MXcomments", no_argument, &(config_stream->remove_comments), 0},
{"MXoptTags", no_argument, &(config_stream->remove_optional_tags), 0},
{"MXtagWS", no_argument, &(config_stream->remove_tag_whitespace), 0},
{0, 0, 0, 0}
};
int option_index = 0;
int c = getopt_long(argc, argv, "kbvi:o:s:", long_options, &option_index);
if (c == -1) {
if (optind != argc) {
HBE_THROW_F(HBE_CLI_TOO_MANY_OPTIONS, "Too many arguments provided");
}
break;
}
switch (c) {
case 'i':
input_path = optarg;
break;
case 'o':
output_path = optarg;
break;
case 's':
HBE_CATCH_F(hbu_streamoptions_parse_and_add_errors_to_suppress, config_stream->suppressed_errors, optarg);
break;
case 40:
nondefault_ex_collapse_whitespace = 1;
config_stream->ex_collapse_whitespace = HBE_CATCH_F(hbu_streamoptions_parse_list_of_tags, optarg);
break;
case 41:
nondefault_ex_destroy_whole_whitespace = 1;
config_stream->ex_destroy_whole_whitespace = HBE_CATCH_F(hbu_streamoptions_parse_list_of_tags, optarg);
break;
case 42:
nondefault_ex_trim_whitespace = 1;
config_stream->ex_trim_whitespace = HBE_CATCH_F(hbu_streamoptions_parse_list_of_tags, optarg);
break;
}
}
if (!nondefault_ex_collapse_whitespace) config_stream->ex_collapse_whitespace = hbu_streamoptions_default_ex_collapse_whitespace();
if (!nondefault_ex_destroy_whole_whitespace) config_stream->ex_destroy_whole_whitespace = hbu_streamoptions_default_ex_destroy_whole_whitespace();
if (!nondefault_ex_trim_whitespace) config_stream->ex_trim_whitespace = hbu_streamoptions_default_ex_trim_whitespace();
if (logging) {
hbl_info_kv_string("Input", input_path);
hbl_info_kv_string("Output", output_path);
hbl_info_kv_boolean("Buffer output until success", config_buffer);
hbl_info_kv_boolean("Keep output file on error", config_keep);
hbu_streamoptions_log(config_stream);
}
hbu_pipe_t pipe = hbu_pipe_create_blank(input_path);
hbu_fstreamin_t input = HBE_CATCH_F(hbu_fstreamin_create, input_path);
hbu_pipe_blank_set_input_fstreamin(pipe, input);
if (config_buffer) {
output_buffer = hbu_list_char_create();
hbu_pipe_blank_set_output_buffer(pipe, output_buffer);
} else {
output = HBE_CATCH_F(hbu_fstreamout_create, output_path);
hbu_pipe_blank_set_output_fstreamout(pipe, output);
}
HBE_CATCH_F(hbs_content, config_stream, pipe, NULL);
if (config_buffer) {
output = HBE_CATCH_F(hbu_fstreamout_create, output_path);
HBE_CATCH_F(hbu_fstreamout_write_buffer, output, output_buffer);
}
finally:
if (err != NULL) {
hbl_error(err);
if (output != NULL && !config_keep && !config_buffer) {
// Delete only after opening output stream (don't delete before existing file has not been touched)
// Don't need to set if $config_buffer, as it won't write anything anyway
if (unlink(output_path)) {
hbl_log(HBL_LOG_WARN, "Failed to delete file %s with error %d", output_path, errno);
} else {
hbl_log(HBL_LOG_INFO, "%s has been deleted", output_path);
}
}
exit(err->code);
}
if (logging) {
hbl_log(HBL_LOG_INFO, "All done!");
}
exit(0);
}

270
src/hb-cli.c Normal file
View File

@ -0,0 +1,270 @@
#pragma once
#include <getopt.h>
#include "./stream/content/html.c"
#include "./__main__.c"
nh_set_str_t hbu_streamoptions_parse_list_of_tags(hbe_err_t *hbe_err, char *argv) {
nh_set_str_t set = NULL;
hb_list_charlist_t list = NULL;
if (argv != NULL && strcmp(argv, "*")) {
return NULL;
}
set = nh_set_str_create();
if (argv == NULL) {
return set;
}
list = hb_list_charlist_create_from_split((hb_proc_char_t *) argv, ',');
for (size_t i = 0; i < list->length; i++) {
hb_list_char_t part = hb_list_charlist_get(list, i);
hb_proc_char_t *part_c = hb_list_char_underlying(part);
if (hb_list_char_get(part, 0) == '$') {
// Set of tags
hb_list_char_shift(part);
HBE_CATCH_F(hbu_streamoptions_parse_and_add_tag_set, (char *) part_c, set);
} else {
// Single tag
if (!hb_rule_tags_check(part_c)) {
HBE_THROW_F(HBE_CLI_INVALID_TAG, "%s is not a standard tag and was provided as part of an argument's value", part_c);
}
nh_set_str_add(set, (char *) hb_list_char_underlying_copy(part));
}
}
finally:
if (list != NULL) {
hb_list_charlist_destroy_from_split(list);
list = NULL;
}
if (*hbe_err != NULL) {
if (set != NULL) {
nh_set_str_destroy(set);
set = NULL;
}
}
return set;
}
void hbu_streamoptions_parse_and_add_errors_to_suppress(hbe_err_t *hbe_err, nh_set_int32_t suppressed_errors, char *argv) {
hb_list_charlist_t list = NULL;
if (argv == NULL) {
return;
}
list = hb_list_charlist_create_from_split((hb_proc_char_t *) argv, ',');
for (size_t i = 0; i < list->length; i++) {
hb_list_char_t part = hb_list_charlist_get(list, i);
if (hb_list_char_compare_lit(part, "MALFORMED_ENTITY") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_MALFORMED_ENTITY);
} else if (hb_list_char_compare_lit(part, "BARE_AMPERSAND") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_BARE_AMPERSAND);
} else if (hb_list_char_compare_lit(part, "INVALID_ENTITY") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_INVALID_ENTITY);
} else if (hb_list_char_compare_lit(part, "NONSTANDARD_TAG") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_NONSTANDARD_TAG);
} else if (hb_list_char_compare_lit(part, "UCASE_ATTR") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_UCASE_ATTR);
} else if (hb_list_char_compare_lit(part, "UCASE_TAG") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_UCASE_TAG);
} else if (hb_list_char_compare_lit(part, "UNQUOTED_ATTR") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_UNQUOTED_ATTR);
} else if (hb_list_char_compare_lit(part, "SELF_CLOSING_TAG") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_SELF_CLOSING_TAG);
} else {
HBE_THROW_F(HBE_CLI_INVALID_SUPPRESSABLE_ERROR, "Unrecognised suppressable error `%s`", hb_list_char_underlying(part));
}
}
finally:
if (list != NULL) {
hb_list_charlist_destroy_from_split(list);
list = NULL;
}
}
void hbu_streamoptions_log(hbu_streamoptions_t opt) {
hbl_info_kv_boolean("Trim `class` attributes", opt->trim_class_attr);
hbl_info_kv_boolean("Decode entities", opt->decode_entities);
hbl_info_kv_boolean("Minify conditional comments", opt->min_conditional_comments);
hbl_info_kv_boolean("Remove attribute quotes", opt->remove_attr_quotes);
hbl_info_kv_boolean("Remove comments", opt->remove_comments);
hbl_info_kv_boolean("Remove optional tags", opt->remove_optional_tags);
hbl_info_kv_boolean("Remove tag whitespace", opt->remove_tag_whitespace);
}
void hbu_streamoptions_parse_and_add_tag_set(hbe_err_t *hbe_err, char *set_name, nh_set_str_t set) {
if (strcmp(set_name, "content") == 0) {
hb_rule_contenttags_add_elems(set);
} else if (strcmp(set_name, "contentfirst") == 0) {
hb_rule_contentfirsttags_add_elems(set);
} else if (strcmp(set_name, "formatting") == 0) {
hb_rule_formattingtags_add_elems(set);
} else if (strcmp(set_name, "layout") == 0) {
hb_rule_layouttags_add_elems(set);
} else if (strcmp(set_name, "specific") == 0) {
hb_rule_specifictags_add_elems(set);
} else if (strcmp(set_name, "heading") == 0) {
hb_rule_headingtags_add_elems(set);
} else if (strcmp(set_name, "media") == 0) {
hb_rule_mediatags_add_elems(set);
} else if (strcmp(set_name, "sectioning") == 0) {
hb_rule_sectioningtags_add_elems(set);
} else if (strcmp(set_name, "void") == 0) {
hb_rule_voidtags_add_elems(set);
} else if (strcmp(set_name, "wss") == 0) {
hb_rule_wsstags_add_elems(set);
} else {
HBE_THROW_V(HBE_CLI_INVALID_TAG_SET, "Unrecognised tag set `%s`", set_name);
}
}
int main(int argc, char **argv) {
hb_init();
hbe_err_t err = NULL;
hbe_err_t *hbe_err = &err;
hbu_fstreamout_t output = NULL;
hb_list_char_t output_buffer = NULL;
// Prepare config
char *input_path = NULL;
char *output_path = NULL;
int logging = 0;
int config_keep = 0;
int config_buffer = 0;
hbu_streamoptions_t config_stream = hbu_streamoptions_create();
int nondefault_ex_collapse_whitespace = 0;
int nondefault_ex_destroy_whole_whitespace = 0;
int nondefault_ex_trim_whitespace = 0;
// Parse arguments
while (1) {
struct option long_options[] = {
{"keep", no_argument, &config_keep, 1},
{"buffer", no_argument, &config_buffer, 1},
{"verbose", no_argument, &logging, 1},
{"input", required_argument, NULL, 'i'},
{"output", required_argument, NULL, 'o'},
{"suppress", required_argument, NULL, 's'},
{"MXcollapseWhitespace", optional_argument, NULL, 40},
{"MXdestroyWholeWhitespace", optional_argument, NULL, 41},
{"MXtrimWhitespace", optional_argument, NULL, 42},
{"MXtrimClassAttr", no_argument, &(config_stream->trim_class_attr), 0},
{"MXdecEnt", no_argument, &(config_stream->decode_entities), 0},
{"MXcondComments", no_argument, &(config_stream->min_conditional_comments), 0},
{"MXattrQuotes", no_argument, &(config_stream->decode_entities), 0},
{"MXcomments", no_argument, &(config_stream->remove_comments), 0},
{"MXoptTags", no_argument, &(config_stream->remove_optional_tags), 0},
{"MXtagWS", no_argument, &(config_stream->remove_tag_whitespace), 0},
{0, 0, 0, 0}
};
int option_index = 0;
int c = getopt_long(argc, argv, "kbvi:o:s:", long_options, &option_index);
if (c == -1) {
if (optind != argc) {
HBE_THROW_F(HBE_CLI_TOO_MANY_OPTIONS, "Too many arguments provided");
}
break;
}
switch (c) {
case 'i':
input_path = optarg;
break;
case 'o':
output_path = optarg;
break;
case 's':
HBE_CATCH_F(hbu_streamoptions_parse_and_add_errors_to_suppress, config_stream->suppressed_errors, optarg);
break;
case 40:
nondefault_ex_collapse_whitespace = 1;
config_stream->ex_collapse_whitespace = HBE_CATCH_F(hbu_streamoptions_parse_list_of_tags, optarg);
break;
case 41:
nondefault_ex_destroy_whole_whitespace = 1;
config_stream->ex_destroy_whole_whitespace = HBE_CATCH_F(hbu_streamoptions_parse_list_of_tags, optarg);
break;
case 42:
nondefault_ex_trim_whitespace = 1;
config_stream->ex_trim_whitespace = HBE_CATCH_F(hbu_streamoptions_parse_list_of_tags, optarg);
break;
}
}
if (!nondefault_ex_collapse_whitespace) config_stream->ex_collapse_whitespace = hbu_streamoptions_default_ex_collapse_whitespace();
if (!nondefault_ex_destroy_whole_whitespace) config_stream->ex_destroy_whole_whitespace = hbu_streamoptions_default_ex_destroy_whole_whitespace();
if (!nondefault_ex_trim_whitespace) config_stream->ex_trim_whitespace = hbu_streamoptions_default_ex_trim_whitespace();
if (logging) {
hbl_info_kv_string("Input", input_path);
hbl_info_kv_string("Output", output_path);
hbl_info_kv_boolean("Buffer output until success", config_buffer);
hbl_info_kv_boolean("Keep output file on error", config_keep);
hbu_streamoptions_log(config_stream);
}
hb_proc_t pipe = hb_proc_create_blank(input_path);
hbu_fstreamin_t input = HBE_CATCH_F(hbu_fstreamin_create, input_path);
hb_proc_blank_set_input_fstreamin(pipe, input);
if (config_buffer) {
output_buffer = hb_list_char_create();
hb_proc_blank_set_output_buffer(pipe, output_buffer);
} else {
output = HBE_CATCH_F(hbu_fstreamout_create, output_path);
hb_proc_blank_set_output_fstreamout(pipe, output);
}
HBE_CATCH_F(hbs_content, config_stream, pipe, NULL);
if (config_buffer) {
output = HBE_CATCH_F(hbu_fstreamout_create, output_path);
HBE_CATCH_F(hbu_fstreamout_write_buffer, output, output_buffer);
}
finally:
if (err != NULL) {
hbl_error(err);
if (output != NULL && !config_keep && !config_buffer) {
// Delete only after opening output stream (don't delete before existing file has not been touched)
// Don't need to set if $config_buffer, as it won't write anything anyway
if (unlink(output_path)) {
hbl_log(HBL_LOG_WARN, "Failed to delete file %s with error %d", output_path, errno);
} else {
hbl_log(HBL_LOG_INFO, "%s has been deleted", output_path);
}
}
exit(err->code);
}
if (logging) {
hbl_log(HBL_LOG_INFO, "All done!");
}
exit(0);
}

1
src/hb-cli.h Normal file
View File

@ -0,0 +1 @@

94
src/hb-config.c Normal file
View File

@ -0,0 +1,94 @@
#pragma once
#include <stddef.h>
#include <stdbool.h>
#include "hb-rule.h"
#include "hb-config.h"
static struct hb_config_ex_s _ex_collapse_whitespace_default;
static struct hb_config_ex_s _ex_destroy_whole_whitespace_default;
static struct hb_config_ex_s _ex_trim_whitespace_default;
// WARNING: Rules must be initialised before calling this function
void hb_config_init(void)
{
nh_set_str ex_collapse_whitespace_set = nh_set_str_create();
hb_rule_wsstags_add_elems(ex_collapse_whitespace_set);
_ex_collapse_whitespace_default = {HB_CONFIG_EX_MODE_DEFAULT,
ex_collapse_whitespace_set};
nh_set_str ex_destroy_whole_whitespace_set = nh_set_str_create();
hb_rule_wsstags_add_elems(ex_destroy_whole_whitespace_set);
hb_rule_contenttags_add_elems(ex_destroy_whole_whitespace_set);
hb_rule_formattingtags_add_elems(ex_destroy_whole_whitespace_set);
_ex_destroy_whole_whitespace_default = {
HB_CONFIG_EX_MODE_DEFAULT, ex_destroy_whole_whitespace_set};
nh_set_str ex_trim_whitespace_set = nh_set_str_create();
hb_rule_wsstags_add_elems(ex_trim_whitespace_set);
hb_rule_formattingtags_add_elems(ex_trim_whitespace_set);
_ex_trim_whitespace_default = {HB_CONFIG_EX_MODE_DEFAULT,
ex_trim_whitespace_set};
}
hb_config_t* hb_config_create(void)
{
hb_config_t* config = malloc(sizeof(struct hb_config_s));
config->ex_collapse_whitespace = _ex_collapse_whitespace_default;
config->ex_destroy_whole_whitespace =
_ex_destroy_whole_whitespace_default;
config->ex_trim_whitespace = _ex_trim_whitespace_default;
config->suppressed_errors = nh_set_int32_create();
config->trim_class_attr = true;
config->decode_entities = true;
config->min_conditional_comments = true;
config->remove_attr_quotes = true;
config->remove_comments = true;
config->remove_optional_tags = true;
config->remove_tag_whitespace = true;
return config;
}
void hb_config_ex_use_none(hb_config_ex_t* config_ex)
{
*config_ex = {HB_CONFIG_EX_MODE_NONE, NULL};
}
void hb_config_ex_use_custom(hb_config_ex_t* config_ex, nh_set_str custom_set)
{
*config_ex = {HB_CONFIG_EX_MODE_CUSTOM, custom_set};
}
void hb_config_ex_use_all(hb_config_ex_t* config_ex)
{
*config_ex = {HB_CONFIG_EX_MODE_ALL};
}
void hb_config_destroy(hb_config_t* opt)
{
nh_set_int32_destroy(opt->suppressed_errors);
free(opt);
}
bool hb_config_supressed_error_check(hb_config_t opt, hb_error_t errcode)
{
return nh_set_int32_has(&opt->suppressed_errors, errcode);
}
bool hb_config_ex_check(hb_config_t* config, hb_proc_char_t* query)
{
switch (config->mode) {
case HB_CONFIG_EX_MODE_ALL:
return true;
case HB_CONFIG_EX_MODE_NONE:
return false;
default:
return nh_set_str_has(config->set, query);
}
if (config->mode == HB_CONFIG_EX_MODE_ALL) {
return true;
}
}

36
src/hb-config.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
typedef enum {
HB_CONFIG_EX_MODE_NONE, // i.e. minify all without exeption
HB_CONFIG_EX_MODE_DEFAULT, // entire struct will not be destroyed
HB_CONFIG_EX_MODE_CUSTOM, // set will be destroyed
HB_CONFIG_EX_MODE_ALL, // i.e. don't minify
} hb_config_ex_mode_t;
typedef struct {
hb_config_ex_mode_t mode;
nh_set_str set;
} hb_config_ex_t;
typedef struct {
hb_config_ex_t ex_collapse_whitespace;
hb_config_ex_t ex_destroy_whole_whitespace;
hb_config_ex_t ex_trim_whitespace;
nh_set_int32 suppressed_errors;
bool trim_class_attributes;
bool decode_entities;
bool remove_attr_quotes;
bool remove_comments;
bool remove_optional_tags;
bool remove_tag_whitespace;
} hb_config_t;
// WARNING: Rules must be initialised before calling this function
void hb_config_init(void);
hb_config_t* hb_config_create(void);
void hb_config_ex_use_none(hb_config_ex_t* config_ex);
void hb_config_ex_use_custom(hb_config_ex_t* config_ex, nh_set_str custom_set);
void hb_config_ex_use_all(hb_config_ex_t* config_ex);
void hb_config_destroy(hb_config_t* opt);
bool hb_config_supressed_error_check(hb_config_t opt, hb_error_t errcode);
bool hb_config_ex_check(hb_config_ex_t* config, hb_proc_char_t* query);

8
src/hb-data.h Normal file
View File

@ -0,0 +1,8 @@
#include <stdint.h>
#include "nicehash/list.h"
#include "nicehash/list-ucp.h"
#include "nicehash/map-str.h"
NH_MAP_STR(int32, int32_t);
NH_MAP_STR(set_str, nh_set_str*);

33
src/hb-error.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
typedef enum {
HBE_NO_ERROR = 0,
HBE_INTERR_UNKNOWN_ENTITY_TYPE = 2,
HBE_INTERR_UNKNOWN_CONTENT_NEXT_STATE,
HBE_CLI_TOO_MANY_OPTIONS = 17,
HBE_CLI_INVALID_TAG_SET,
HBE_CLI_INVALID_TAG,
HBE_CLI_INVALID_SUPPRESSABLE_ERROR,
HBE_IO_FOPEN_FAIL = 33,
HBE_IO_FCLOSE_FAIL,
HBE_IO_FREAD_FAIL,
HBE_IO_FWRITE_FAIL,
HBE_PARSE_MALFORMED_ENTITY = 65,
HBE_PARSE_BARE_AMPERSAND,
HBE_PARSE_INVALID_ENTITY,
HBE_PARSE_NONSTANDARD_TAG,
HBE_PARSE_UCASE_TAG,
HBE_PARSE_UCASE_ATTR,
HBE_PARSE_UNQUOTED_ATTR,
HBE_PARSE_ILLEGAL_CHILD,
HBE_PARSE_UNCLOSED_TAG,
HBE_PARSE_SELF_CLOSING_TAG,
HBE_PARSE_NO_SPACE_BEFORE_ATTR,
HBE_PARSE_UNEXPECTED_END,
HBE_PARSE_EXPECTED_NOT_FOUND,
} hb_error_t;

24
src/hb-file-in.c Normal file
View File

@ -0,0 +1,24 @@
#include <errno.h>
#include "../char/char.c"
#include "../execution/error.c"
#include "./__base__.c"
HBU_FSTREAM_BUILD_INFRA(in, "r", "read", "reading", stdin)
hb_eod_char_t hbu_fstreamin_read(hbe_err_t* hbe_err, hbu_fstreamin_t fstreamin)
{
hb_proc_char_t c;
if (fread(&c, SIZEOF_CHAR, 1, fstreamin->fd) != SIZEOF_CHAR) {
if (ferror(fstreamin->fd)) {
HBE_THROW(HBE_IO_FREAD_FAIL,
"Failed to read input file %s",
fstreamin->name);
}
// Must be EOF
return HB_EOD;
}
return c;
}

32
src/hb-file-out.c Normal file
View File

@ -0,0 +1,32 @@
#include <errno.h>
#include "../execution/error.c"
#include "../list/char.c"
#include "./__base__.c"
HBU_FSTREAM_BUILD_INFRA(out, "w", "write", "writing", stdout)
static void _hbu_fstreamout_fwrite(hbe_err_t* hbe_err,
hbu_fstreamout_t fstreamout,
hb_proc_char_t* source, size_t length)
{
if (fwrite(source, SIZEOF_CHAR, length, fstreamout->fd)
!= SIZEOF_CHAR * length) {
HBE_THROW_V(HBE_IO_FWRITE_FAIL,
"Failed to write to output file %s",
fstreamout->name);
}
}
void hbu_fstreamout_write_buffer(hbe_err_t* hbe_err,
hbu_fstreamout_t fstreamout,
hb_list_char_t buffer)
{
HBE_CATCH_V(_hbu_fstreamout_fwrite, fstreamout,
hb_list_char_underlying(buffer), buffer->length);
}
void hbu_fstreamout_write(hbe_err_t* hbe_err, hbu_fstreamout_t fstreamout,
hb_proc_char_t c)
{
HBE_CATCH_V(_hbu_fstreamout_fwrite, fstreamout, &c, 1);
}

51
src/hb-file.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <errno.h>
typedef enum {
HB_FILE_ENC_UTF_8,
HB_FILE_ENC_UTF_16,
} hb_file_enc_t;
#define HB_FILE(type, mode, noun, verb, std) \
typedef struct { \
char const* name; \
hb_file_enc_t encoding; \
FILE* fd; \
} hb_file_##type##_t; \
\
hb_file_##type##_t* hb_file_##type##_create(char* path) \
{ \
hb_file_##type##_t* fstream = \
malloc(sizeof(hb_file_##type##_t)); \
\
if (path == NULL) { \
fstream->name = #std; \
fstream->fd = std; \
} else { \
fstream->name = path; \
\
FILE* fd = fopen(path, mode); \
\
if (fd == NULL) { \
return NULL; \
} \
\
fstream->fd = fd; \
} \
\
return fstream; \
} \
\
void hb_file_##type##_destroy(hbe_err_t* hbe_err, \
hb_file_##type##_t fstream) \
{ \
if (fclose(fstream->fd) == EOF) { \
HBE_THROW_V(HBE_IO_FCLOSE_FAIL, \
"Failed to close " noun \
" stream for file %s with error %d", \
fstream->name, errno); \
} \
\
free(fstream); \
}

144
src/hb-proc-accept.c Normal file
View File

@ -0,0 +1,144 @@
/**
* Accepts the next character.
* Will cause an error if already at end.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return next character
* @throws on read/write error or HBE_PARSE_UNEXPECTED_END
*/
hb_proc_char_t hb_proc_accept(hbe_err_t *hbe_err, hb_proc_t pipe) {
hb_eod_char_t c = HBE_CATCH(_hb_proc_read_from_buffer_or_input, pipe);
HBE_CATCH(_hb_proc_assert_not_eoi, c);
_hb_proc_update_pos(pipe, c);
HBE_CATCH(_hb_proc_write_to_output, pipe, c);
return c;
}
/**
* Accepts the next <code>count</code> characters.
* Requires at least <code>count</code> characters remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param count amount of characters
* @throws on read/write error or HBE_PARSE_UNEXPECTED_END
*/
void hb_proc_accept_count(hbe_err_t *hbe_err, hb_proc_t pipe, size_t count) {
for (size_t i = 0; i < count; i++) {
HBE_CATCH_V(hb_proc_accept, pipe);
}
}
/**
* Accepts the following character if it is <code>c</code>.
* Won't match or cause an error if there are no characters remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param c character to match
* @return 0 if nothing was accepted, 1 otherwise
* @throws on read/write error
*/
int hb_proc_accept_if(hbe_err_t *hbe_err, hb_proc_t pipe, hb_proc_char_t c) {
hb_eod_char_t n = HBE_CATCH(hb_proc_peek_eoi, pipe);
if (n == HB_EOD || n != c) {
return 0;
}
HBE_CATCH(hb_proc_accept, pipe);
return 1;
}
/**
* Accepts the following characters if they match <code>match</code>.
* Won't match or cause an error if there are not enough characters remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match characters to match
* @return 0 if nothing was accepted, 1 otherwise
* @throws on read/write error
*/
int hb_proc_accept_if_matches(hbe_err_t *hbe_err, hb_proc_t pipe, const char *match) {
size_t matchedlen = HBE_CATCH(hb_proc_matches, pipe, match);
int matched = matchedlen > 0;
if (matched) {
HBE_CATCH(hb_proc_accept_count, pipe, matchedlen);
}
return matched;
}
/**
* Accepts the following characters if they are either "\r", "\r\n", or "\n".
* Won't cause an error if insufficient amount of characters left.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return amount of characters matched
* @throws on read/write error
*/
size_t hb_proc_accept_if_matches_line_terminator(hbe_err_t *hbe_err, hb_proc_t pipe) {
size_t matchedlen = HBE_CATCH(hb_proc_matches_line_terminator, pipe);
if (matchedlen) {
HBE_CATCH(hb_proc_accept_count, pipe, matchedlen);
}
return matchedlen;
}
/**
* Accepts the following character if it satisfies the predicate <code>pred</code>.
* Won't do anything if already at the end.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param pred predicate
* @return 0 if nothing was accepted, 1 otherwise
* @throws on read/write error
*/
int hb_proc_accept_if_predicate(hbe_err_t *hbe_err, hb_proc_t pipe, hb_proc_predicate_t pred) {
hb_eod_char_t c = HBE_CATCH(hb_proc_peek_eoi, pipe);
if (c == HB_EOD || !(*pred)(c)) {
return 0;
}
HBE_CATCH(hb_proc_accept, pipe);
return 1;
}
/**
* Accepts every following character until one dissatisfies the predicate <code>pred</code>,
* or the end is reached.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param pred predicate
* @return amount of characters accepted
* @throws on read/write error
*/
size_t hb_proc_accept_while_predicate(hbe_err_t *hbe_err, hb_proc_t pipe, hb_proc_predicate_t pred) {
size_t count = 0;
while (1) {
int matched = HBE_CATCH(hb_proc_accept_if_predicate, pipe, pred);
if (!matched) {
break;
}
count++;
}
return count;
}

262
src/hb-proc-core.c Normal file
View File

@ -0,0 +1,262 @@
#pragma once
#include <stdbool.h>
#include <string.h>
#include "../../rule/char/ucalpha.c"
#include "../char/char.c"
#include "../execution/error.c"
#include "../list/char.c"
#include "../fstream/fstreamin.c"
#include "../fstream/fstreamout.c"
// Use macro to prevent having to allocate (and therefore free/manage) memory
#define HB_PROC_FORMAT_WITH_POS(fn, a, format, ...) fn(a, format " at %s [line %d, column %d]", __VA_ARGS__, proc->name, proc->line, proc->column);
/**
* Creates an error using a message with the current position appended.
*
* @param proc proc
* @param errcode error code
* @param reason message
* @return error
*/
hbe_err_t hb_proc_error(hb_proc_t* proc, hb_error_t errcode, const char *reason, ...) {
va_list args;
va_start(args, reason);
char *msg = calloc(HB_PROC_MAX_ERR_MSG_LEN + 1, SIZEOF_CHAR);
vsnprintf(msg, HB_PROC_MAX_ERR_MSG_LEN, reason, args);
va_end(args);
hbe_err_t err = HBU_FN_FORMAT_WITH_POS(hbe_err_create, errcode, "%s", msg);
free(msg);
return err;
}
/**
* Reads from input.
*
* @param hbe_err pointer to hbe_err_t
* @param proc proc
* @throws on read error
*/
static hb_eod_char_t _hb_proc_read_from_input(hbe_err_t *hbe_err, hb_proc_t* proc) {
hb_eod_char_t c = HBE_CATCH((*proc->reader), proc->input);
if (c == HB_EOD) {
proc->EOI = 1;
}
return c;
}
/**
* Ensures the buffer has loaded at least the next <code>offset</code> characters, or the remaining
* characters from input if there are less than <code>offset</code> characters left.
*
* @param hbe_err pointer to hbe_err_t
* @param proc proc
* @throws on read error
*/
static void _hb_proc_ensure_buffer(hbe_err_t *hbe_err, hb_proc_t* proc, size_t offset) {
if (proc->EOI) {
return;
}
size_t current = proc->buffer->length;
if (offset <= current) {
return;
}
size_t gap = offset - current;
for (size_t i = 0; i < gap; i++) {
hb_eod_char_t c = HBE_CATCH_V(_hb_proc_read_from_input, proc);
if (c == HB_EOD) {
// EOI flag already set by _hb_proc_read_from_input
return;
}
hb_list_char_append(proc->buffer, c);
}
}
/**
* Gets the next character, whether it's by shifting the buffer or reading from input.
*
* @param hbe_err pointer to hbe_err_t
* @param proc proc
* @throws on read error
*/
static hb_eod_char_t _hb_proc_read_from_buffer_or_input(hbe_err_t *hbe_err, hb_proc_t* proc) {
if (proc->EOI) {
return HB_EOD;
}
if (proc->buffer->length) {
return hb_list_char_shift(proc->buffer);
}
return HBE_CATCH(_hb_proc_read_from_input, proc);
}
static void _hb_proc_update_pos(hb_proc_t* proc, hb_proc_char_t c) {
switch (c) {
case '\r':
proc->CR = true;
proc->line++;
proc->column = 0;
break;
case '\n':
if (!proc->CR) {
proc->line++;
proc->column = 0;
} else {
proc->CR = false;
}
break;
default:
proc->column++;
proc->CR = false;
}
}
/**
* Ensures that the provided character is not @{link HB_EOD}, and causes an error otherwise.
*
* @param c character to test for <code>HB_EOD</code>
* @throws HBE_PARSE_UNEXPECTED_END if <code>c</code> is <code>HB_EOD</code>
*/
static void _hb_proc_assert_not_eoi(hbe_err_t *hbe_err, hb_eod_char_t c) {
if (c == HB_EOD) {
HBE_THROW_V(HBE_PARSE_UNEXPECTED_END, "Unexpected end of input");
}
}
/**
* Writes a character to the redirect, if enabled, otherwise output, of a proc,
* unless the output is masked.
*
* @param hbe_err pointer to hbe_err_t
* @param proc proc
* @param c character to write
* @return a freshly-created proc
* @throws on write error
*/
static void _hb_proc_write_to_output(hbe_err_t *hbe_err, hb_proc_t* proc, hb_proc_char_t c) {
if (!proc->mask) {
hb_list_char_t redirect = proc->redirect;
if (redirect != NULL) {
hb_list_char_append(redirect, c);
} else {
HBE_CATCH_V((*proc->writer), proc->output, c);
}
}
}
/*
*
* INSTANCE MANAGEMENT FUNCTIONS
*
*/
/**
* Allocates memory for a proc, and creates one with provided arguments.
*
* @param input input
* @param reader reader
* @param name name
* @param output output
* @param writer writer
* @return a freshly-created proc
*/
hb_proc_t* hb_proc_create_blank(char *name) {
hb_proc_t* proc = calloc(1, sizeof(hb_proc_t));
proc->name = name;
proc->input = NULL;
proc->reader = NULL;
proc->EOI = false;
proc->line = 1;
proc->column = 0;
proc->CR = false;
proc->output = NULL;
proc->writer = NULL;
proc->buffer = nh_list_ucp_create();
proc->mask = false;
proc->redirect = NULL;
return proc;
}
/**
* Frees all memory associated with a proc.
*
* @param proc proc
*/
void hb_proc_destroy(hb_proc_t* proc) {
nh_list_ucp_destroy(proc->buffer);
free(proc);
}
/**
* Enables or disables the output mask.
* When the output mask is enabled, all writes are simply discarded and not actually written to output.
*
* @param proc proc
* @param mask 1 to enable, 0 to disable
* @return previous state
*/
int hb_proc_toggle_output_mask(hb_proc_t* proc, int mask) {
int current = proc->mask;
proc->mask = mask;
return current;
}
/**
* Enables or disables the output redirect.
* When the output redirect is enabled, all writes are written to a buffer instead of the output.
*
* @param proc proc
* @param redirect buffer to redirect writes to, or NULL to disable
*/
void hb_proc_set_redirect(hb_proc_t* proc, hb_list_char_t redirect) {
proc->redirect = redirect;
}
void hb_proc_blank_set_input_fstreamin(hb_proc_t* proc, hbu_fstreamin_t fstreamin) {
proc->input = fstreamin;
proc->reader = (hb_proc_reader_cb_t) &hbu_fstreamin_read;
}
// Wrapper function for hb_list_char_shift to make it compatible with hb_proc_reader_cb_t
static hb_eod_char_t hb_proc_read_from_list_char_input(hbe_err_t *hbe_err, hb_list_char_t input) {
(void) hbe_err;
return hb_list_char_shift(input);
}
void hb_proc_blank_set_input_buffer(hb_proc_t* proc, hb_list_char_t buf) {
proc->input = buf;
proc->reader = (hb_proc_reader_cb_t) &hb_proc_read_from_list_char_input;
}
static void hb_proc_blank_set_output_fstreamout(hb_proc_t* proc, hbu_fstreamout_t fstreamout) {
proc->output = fstreamout;
proc->writer = (hb_proc_writer_cb_t) &hbu_fstreamout_write;
}
// Wrapper function for hb_list_char_append to make it compatible with hb_proc_writer_cb_t
void hb_proc_write_to_list_char_output(hbe_err_t *hbe_err, hb_list_char_t output, hb_proc_char_t c) {
(void) hbe_err;
hb_list_char_append(output, c);
}
void hb_proc_blank_set_output_buffer(hb_proc_t* proc, hb_list_char_t buf) {
proc->output = buf;
proc->writer = (hb_proc_writer_cb_t) &hb_proc_write_to_list_char_output;
}

75
src/hb-proc-matches.c Normal file
View File

@ -0,0 +1,75 @@
/**
* Checks if the next sequence of characters is the null-terminated character array <code>match</code>.
* Won't cause an error if insufficient amount of characters left.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match characters to match
* @return amount of characters matched, which should be equal to <code>strlen(match)</code>
* @throws on read error
*/
size_t hb_proc_matches(hbe_err_t *hbe_err, hb_proc_t pipe, const char *match) {
size_t matchlen = strlen(match);
for (size_t i = 0; i < matchlen; i++) {
hb_eod_char_t c = HBE_CATCH(hb_proc_peek_eoi_offset, pipe, i + 1);
if (c == HB_EOD || c != match[i]) {
return 0;
}
}
return matchlen;
}
/**
* Checks if the next sequence of characters matches the null-terminated character array <code>match</code>
* of lowercase characters case-insensitively.
* Won't cause an error if insufficient amount of characters left.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match characters to match case-insensitively
* @return amount of characters matched, which should be equal to <code>strlen(match)</code>
* @throws on read error
*/
size_t hb_proc_matches_i(hbe_err_t *hbe_err, hb_proc_t pipe, const char *match) {
size_t matchlen = strlen(match);
for (size_t i = 0; i < matchlen; i++) {
hb_eod_char_t c = HBE_CATCH(hb_proc_peek_eoi_offset, pipe, i + 1);
if (!(
c != HB_EOD &&
(
c == match[i] ||
(hb_rule_ucalpha_check(c) && (c + 32) == match[i])
)
)) {
return 0;
}
}
return matchlen;
}
/**
* Checks if the next sequence of characters is "\r", "\n", or "\r\n".
* Won't cause an error if insufficient amount of characters left.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return amount of characters matched
* @throws on read error
*/
size_t hb_proc_matches_line_terminator(hbe_err_t *hbe_err, hb_proc_t pipe) {
// Can't chain into HBE_CATCH(...) || HBE_CATCH(...) || ...
// as HBE_CATCH needs auxiliary statement
// `\r\n` must be before `\r`
size_t crlf = HBE_CATCH(hb_proc_matches, pipe, "\r\n");
if (crlf) return crlf;
size_t cr = HBE_CATCH(hb_proc_matches, pipe, "\r");
if (cr) return cr;
return HBE_CATCH(hb_proc_matches, pipe, "\n");
}

78
src/hb-proc-peek.c Normal file
View File

@ -0,0 +1,78 @@
/**
* Gets the next character.
* If it's the end, {@link HB_EOD} is returned.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return character or {@link HB_EOD}
* @throws on read error
*/
hb_eod_char_t hb_proc_peek_eoi(hbe_err_t *hbe_err, hb_proc_t pipe) {
// OPTIMISATION: Read directly from buffer if available (no need to unshift later)
if (pipe->buffer->length) {
return hb_list_char_get(pipe->buffer, 0);
}
hb_eod_char_t c = HBE_CATCH(_hb_proc_read_from_buffer_or_input, pipe);
if (c != HB_EOD) {
hb_list_char_unshift(pipe->buffer, c);
}
return c;
}
/**
* Gets the next character.
* Will cause an error if it's the end and there is no next character.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return character
* @throws on read error or HBE_PARSE_UNEXPECTED_END
*/
hb_proc_char_t hb_proc_peek(hbe_err_t *hbe_err, hb_proc_t pipe) {
hb_eod_char_t c = HBE_CATCH(hb_proc_peek_eoi, pipe);
HBE_CATCH(_hb_proc_assert_not_eoi, c);
return c;
}
/**
* Gets the <i>n</i>th character from current, where <i>n</i> is <code>offset</code>.
* When <code>offset</code> is 1, the next character is returned (equivalent to {@link hb_proc_peek_eoi_offset}).
* If <code>offset</code> is after the last character, {@link HB_EOD} is returned.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param offset position of character to get
* @return character or {@link HB_EOD}
* @throws on read error
*/
hb_eod_char_t hb_proc_peek_eoi_offset(hbe_err_t *hbe_err, hb_proc_t pipe, size_t offset) {
HBE_CATCH(_hb_proc_ensure_buffer, pipe, offset);
hb_eod_char_t c = hb_list_char_get(pipe->buffer, offset - 1);
return c;
}
/**
* Gets the <i>n</i>th character from current, where <i>n</i> is <code>offset</code>.
* When <code>offset</code> is 1, the next character is returned (equivalent to {@link hb_proc_peek_offset}).
* An error will be caused if <code>offset</code> is after the last character.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param offset position of character to get
* @return character
* @throws on read error or HBE_PARSE_UNEXPECTED_END
*/
hb_proc_char_t hb_proc_peek_offset(hbe_err_t *hbe_err, hb_proc_t pipe, size_t offset) {
hb_eod_char_t c = HBE_CATCH(hb_proc_peek_eoi_offset, pipe, offset);
HBE_CATCH(_hb_proc_assert_not_eoi, c);
return c;
}

112
src/hb-proc-require.c Normal file
View File

@ -0,0 +1,112 @@
/**
* Requires the next character to be <code>c</code>.
* The matched character is written to output.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param c character to match
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
void hb_proc_require(hbe_err_t *hbe_err, hb_proc_t pipe, hb_proc_char_t c) {
hb_proc_char_t n = HBE_CATCH_V(hb_proc_accept, pipe);
if (c != n) {
hb_proc_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected `%c` (0x%x), got `%c` (0x%x)", c, c, n, n);
}
}
/**
* Requires the next character to be <code>c</code>.
* The matched character is skipped over and NOT written to output, and also returned.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param c character to match
* @return matched character
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
hb_proc_char_t hb_proc_require_skip(hbe_err_t *hbe_err, hb_proc_t pipe, hb_proc_char_t c) {
hb_proc_char_t n = HBE_CATCH(hb_proc_skip, pipe);
if (c != n) {
hb_proc_THROW(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected `%c` (0x%x), got `%c` (0x%x) at %s", c, c, n, n);
}
return n;
}
/**
* Requires the next character to satisfy the predicate <code>pred</code>.
* The matched character is written to output.
* If not matched, the error message will describe the expected output using <code>name</code>.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param pred predicate
* @param name what to output in the error message to describe the requirement
* @return required character
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
hb_proc_char_t hb_proc_require_predicate(hbe_err_t *hbe_err, hb_proc_t pipe, hb_proc_predicate_t pred, const char *name) {
hb_proc_char_t n = HBE_CATCH(hb_proc_accept, pipe);
if (!(*pred)(n)) {
hb_proc_THROW(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected %s, got `%c` (0x%x)", name, n, n);
}
return n;
}
/**
* Requires the next character to satisfy the predicate <code>pred</code>.
* The matched character is skipped over and NOT written to output.
* If not matched, the error message will describe the expected output using <code>name</code>.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param pred predicate
* @param name what to output in the error message to describe the requirement
* @return required character
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
hb_proc_char_t hb_proc_require_skip_predicate(hbe_err_t *hbe_err, hb_proc_t pipe, hb_proc_predicate_t pred, const char *name) {
hb_proc_char_t n = HBE_CATCH(hb_proc_skip, pipe);
if (!(*pred)(n)) {
hb_proc_THROW(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected %s, got `%c` (0x%x)", name, n, n);
}
return n;
}
/**
* Requires the next sequence of characters to be equal to <code>match</code>.
* Matched characters are written to output.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match sequence of characters to require
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
void hb_proc_require_match(hbe_err_t *hbe_err, hb_proc_t pipe, const char *match) {
int matches = HBE_CATCH_V(hb_proc_accept_if_matches, pipe, match);
if (!matches) {
hb_proc_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected `%s`", match);
}
}
/**
* Requires the next sequence of characters to be equal to <code>match</code>.
* Matched characters are skipped over and NOT written to output.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match sequence of characters to require
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
void hb_proc_require_skip_match(hbe_err_t *hbe_err, hb_proc_t pipe, const char *match) {
int matches = HBE_CATCH_V(hb_proc_skip_if_matches, pipe, match);
if (!matches) {
hb_proc_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected `%s`", match);
}
}

103
src/hb-proc-skip.c Normal file
View File

@ -0,0 +1,103 @@
/**
* Skips over the next character.
* Requires that the file has at least one character remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return skipped character
* @throws on read error or HBE_PARSE_UNEXPECTED_END
*/
hb_proc_char_t hb_proc_skip(hbe_err_t *hbe_err, hb_proc_t pipe) {
hb_eod_char_t c = HBE_CATCH(_hb_proc_read_from_buffer_or_input, pipe);
HBE_CATCH(_hb_proc_assert_not_eoi, c);
_hb_proc_update_pos(pipe, c);
return c;
}
/**
* Skips over the next <code>amount</code> characters.
* Requires that the file has at least <code>amount</code> characters remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param amount amount of characters to skip
* @throws on read error or HBE_PARSE_UNEXPECTED_END
*/
void hb_proc_skip_amount(hbe_err_t *hbe_err, hb_proc_t pipe, size_t amount) {
for (size_t i = 0; i < amount; i++) {
HBE_CATCH_V(hb_proc_skip, pipe);
}
}
/**
* Skips over the following character if it is <code>c</code>.
* Won't cause an error if the end is reached.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param c character to skip if next
* @return 1 if skipped, 0 otherwise
* @throws on read error
*/
int hb_proc_skip_if(hbe_err_t *hbe_err, hb_proc_t pipe, hb_proc_char_t c) {
hb_eod_char_t n = HBE_CATCH(hb_proc_peek_eoi, pipe);
if (n == HB_EOD || n != c) {
return 0;
}
HBE_CATCH(hb_proc_skip, pipe);
return 1;
}
/**
* Skips over every following character until one dissatisfies the predicate <code>pred</code>,
* or the end is reached.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param pred predicate
* @return amount of characters skipped
* @throws on read error
*/
size_t hb_proc_skip_while_predicate(hbe_err_t *hbe_err, hb_proc_t pipe, hb_proc_predicate_t pred) {
size_t count = 0;
while (1) {
hb_eod_char_t c = HBE_CATCH(hb_proc_peek_eoi, pipe);
if (c == HB_EOD || !(*pred)(c)) {
break;
}
HBE_CATCH(hb_proc_skip, pipe);
count++;
}
return count;
}
/**
* Skips over the next sequence of characters if they are <code>match</code>.
* Requires that the file has at least the length of <code>match</code> characters remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match sequence of characters to match
* @return 0 if not matched, 1 otherwise
* @throws on read error or HBE_PARSE_UNEXPECTED_END
*/
int hb_proc_skip_if_matches(hbe_err_t *hbe_err, hb_proc_t pipe, const char *match) {
size_t matchedlen = HBE_CATCH(hb_proc_matches, pipe, match);
int matched = matchedlen > 0;
if (matched) {
HBE_CATCH(hb_proc_skip_amount, pipe, matchedlen);
}
return matched;
}

69
src/hb-proc-write.c Normal file
View File

@ -0,0 +1,69 @@
/**
* Writes a character.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param c character
* @throws on write error
*/
void hb_proc_write(hbe_err_t *hbe_err, hb_proc_t pipe, hb_proc_char_t c) {
HBE_CATCH_V(_hb_proc_write_to_output, pipe, c);
}
/**
* Writes a buffer.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param buffer buffer
* @throws on write error
*/
void hb_proc_write_buffer(hbe_err_t *hbe_err, hb_proc_t pipe, hb_list_char_t buffer) {
for (size_t i = 0; i < buffer->length; i++) {
HBE_CATCH_V(_hb_proc_write_to_output, pipe, hb_list_char_get(buffer, i));
}
}
/**
* Writes a character from its Unicode code point in UTF-8 encoding, which may be multiple bytes.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param code_point Unicode code point
* @return amount of bytes written (0 if invalid code point)
* @throws on write error
*/
// Logic copied from https://gist.github.com/MightyPork/52eda3e5677b4b03524e40c9f0ab1da5
size_t hb_proc_write_unicode(hbe_err_t *hbe_err, hb_proc_t pipe, uint32_t code_point) {
if (code_point <= 0x7F) {
// Plain ASCII
HBE_CATCH(hb_proc_write, pipe, (hb_proc_char_t) code_point);
return 1;
}
if (code_point <= 0x07FF) {
// 2-byte unicode
HBE_CATCH(hb_proc_write, pipe, (hb_proc_char_t) (((code_point >> 6) & 0x1F) | 0xC0));
HBE_CATCH(hb_proc_write, pipe, (hb_proc_char_t) (((code_point >> 0) & 0x3F) | 0x80));
return 2;
}
if (code_point <= 0xFFFF) {
// 3-byte unicode
HBE_CATCH(hb_proc_write, pipe, (hb_proc_char_t) (((code_point >> 12) & 0x0F) | 0xE0));
HBE_CATCH(hb_proc_write, pipe, (hb_proc_char_t) (((code_point >> 6) & 0x3F) | 0x80));
HBE_CATCH(hb_proc_write, pipe, (hb_proc_char_t) (((code_point >> 0) & 0x3F) | 0x80));
return 3;
}
if (code_point <= 0x10FFFF) {
// 4-byte unicode
HBE_CATCH(hb_proc_write, pipe, (hb_proc_char_t) (((code_point >> 18) & 0x07) | 0xF0));
HBE_CATCH(hb_proc_write, pipe, (hb_proc_char_t) (((code_point >> 12) & 0x3F) | 0x80));
HBE_CATCH(hb_proc_write, pipe, (hb_proc_char_t) (((code_point >> 6) & 0x3F) | 0x80));
HBE_CATCH(hb_proc_write, pipe, (hb_proc_char_t) (((code_point >> 0) & 0x3F) | 0x80));
return 4;
}
return 0;
}

73
src/hb-proc.h Normal file
View File

@ -0,0 +1,73 @@
#pragma once
#include <setjmp.h>
#include "hb-data.h"
#include "hb-config.h"
typedef int32_t hb_proc_char_t;
#define HB_PROC_CHAR_EOD -1 // End Of Data
#define HB_PROC_CHAR_SIZE sizeof(hb_proc_char_t)
typedef bool hb_proc_predicate_t(hb_proc_char_t);
// Reader and writer callbacks. The last parameter is a pointer to an error
// message. If the last parameter is not NULL, it is assumed an error occurred.
// The error message WILL BE free'd by the callee automatically, so ensure the
// message was created using malloc or strdup, and is not free'd by the function
// or anything else afterwards.
typedef hb_proc_char_t hb_proc_reader_t(void*, char**);
typedef void hb_proc_writer_t(void*, hb_proc_char_t, char**);
#define HB_PROC_MEMORY_CREATE(name) \
hb_proc_list_memory_instance_add_right_and_return( \
config->memory_instances, name##_create()); \
hb_proc_list_memory_destructor_add_right( \
config->memory_destructors, \
(hb_proc_memory_destructor_t*) &name##_destroy);
NH_LIST(hb_proc_list_memory_instance, void*, sizeof(void*), void*, NULL);
void* hb_proc_list_memory_instance_add_right_and_return(
hb_proc_list_memory_instance*, void*);
typedef void hb_proc_memory_destructor_t(void*);
NH_LIST(hb_proc_list_memory_destructor, hb_proc_memory_destructor_t*,
sizeof(hb_proc_memory_destructor_t*), hb_proc_memory_destructor_t*,
NULL);
#define HB_PROC_ERROR_MESSAGE_SIZE 1024
typedef struct {
hb_error_t code;
char* message;
} hb_proc_result_t;
typedef struct {
char* name;
jmp_buf start;
hb_proc_list_memory_instance* memory_instances;
hb_proc_list_memory_destructor* memory_destructors;
void* input;
hb_proc_reader_t* reader;
bool EOI;
int line;
int column;
bool CR;
void* output;
hb_proc_writer_t* writer;
nh_list_ucp* buffer;
bool mask;
nh_list_ucp* redirect;
hb_config_t config;
} hb_proc_t;
hb_proc_t* hb_proc_create_blank(char* name);
void hb_proc_result_destroy(hb_proc_result_t* result);
hb_proc_result_t* hb_proc_start(hb_proc_t* proc);
void _hb_proc_error(hb_proc_t* proc, hb_error_t code, char const* format, ...);

View File

@ -1,83 +1,83 @@
#include "./attrval.c"
#include "../entity/entity.c"
hbs_attr_val_type_t hbs_quoteattrval(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe, int collapse_and_trim_whitespace) {
hbs_attr_val_type_t hbs_quoteattrval(hbe_err_t *hbe_err, hbu_streamoptions_t so, hb_proc_t pipe, int collapse_and_trim_whitespace) {
hbs_attr_val_type_t rv = 0;
int should_remove_attr_quotes = so->remove_attr_quotes;
hbu_list_char_t value = NULL;
hb_list_char_t value = NULL;
if (should_remove_attr_quotes) {
value = hbu_list_char_create();
hbu_pipe_set_output_redirect(pipe, value);
value = hb_list_char_create();
hb_proc_set_redirect(pipe, value);
}
hb_char_t quote_char = HBE_CATCH_F(hbu_pipe_require_skip_predicate, pipe, &hbr_attrvalquote_check, "attribute value quote");
hb_proc_char_t quote_char = HBE_CATCH_F(hb_proc_require_skip_predicate, pipe, &hb_rule_attrvalquote_check, "attribute value quote");
if (!should_remove_attr_quotes) {
// Not buffering, so directly output
HBE_CATCH_F(hbu_pipe_write, pipe, quote_char);
HBE_CATCH_F(hb_proc_write, pipe, quote_char);
}
int whitespace = 0;
if (collapse_and_trim_whitespace) {
HBE_CATCH_F(hbu_pipe_skip_while_predicate, pipe, &hbr_whitespace_check);
HBE_CATCH_F(hb_proc_skip_while_predicate, pipe, &hb_rule_whitespace_check);
}
int not_empty = 0;
int can_be_unquoted = 1;
while (1) {
hb_char_t c = HBE_CATCH_F(hbu_pipe_peek, pipe);
hb_proc_char_t c = HBE_CATCH_F(hb_proc_peek, pipe);
if (c == quote_char) {
break;
}
not_empty = 1;
can_be_unquoted = can_be_unquoted && hbr_unquotedattrval_check(c);
can_be_unquoted = can_be_unquoted && hb_rule_unquotedattrval_check(c);
if (collapse_and_trim_whitespace && hbr_whitespace_check(c)) {
if (collapse_and_trim_whitespace && hb_rule_whitespace_check(c)) {
whitespace = 1;
HBE_CATCH_F(hbu_pipe_skip, pipe);
HBE_CATCH_F(hb_proc_skip, pipe);
} else {
if (whitespace) {
whitespace = 0;
HBE_CATCH_F(hbu_pipe_write, pipe, ' ');
HBE_CATCH_F(hb_proc_write, pipe, ' ');
}
if (c == '&') {
// Call hbs_entity even if not decoding entities, as it will validate the entity
HBE_CATCH_F(hbs_entity, so, pipe);
} else {
HBE_CATCH_F(hbu_pipe_accept, pipe);
HBE_CATCH_F(hb_proc_accept, pipe);
}
}
}
can_be_unquoted = not_empty && can_be_unquoted;
HBE_CATCH_F(hbu_pipe_require_skip, pipe, quote_char);
HBE_CATCH_F(hb_proc_require_skip, pipe, quote_char);
if (!should_remove_attr_quotes) {
// Not buffering, so directly output
HBE_CATCH_F(hbu_pipe_write, pipe, quote_char);
HBE_CATCH_F(hb_proc_write, pipe, quote_char);
HB_RETURN_F(HBS_ATTR_QUOTED);
} else {
hbu_pipe_set_output_redirect(pipe, NULL);
hb_proc_set_redirect(pipe, NULL);
if (!can_be_unquoted) {
// Wrap in braces as macro will add auxiliary statement
HBE_CATCH_F(hbu_pipe_write, pipe, quote_char);
HBE_CATCH_F(hb_proc_write, pipe, quote_char);
}
HBE_CATCH_F(hbu_pipe_write_buffer, pipe, value);
HBE_CATCH_F(hb_proc_write_buffer, pipe, value);
if (!can_be_unquoted) {
// Wrap in braces as macro will add auxiliary statement
HBE_CATCH_F(hbu_pipe_write, pipe, quote_char);
HBE_CATCH_F(hb_proc_write, pipe, quote_char);
}
HB_RETURN_F(can_be_unquoted ? HBS_ATTR_UNQUOTED : HBS_ATTR_QUOTED);
}
finally:
if (value != NULL) {
hbu_list_char_destroy(value);
hb_list_char_destroy(value);
value = NULL;
}
return rv;

View File

@ -1,15 +1,15 @@
#include "../entity/entity.c"
void hbs_unquoteattrval(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe) {
// Can't use hbu_pipe_require_predicate, as first char might be `&`,
void hbs_unquoteattrval(hbe_err_t *hbe_err, hbu_streamoptions_t so, hb_proc_t pipe) {
// Can't use hb_proc_require_predicate, as first char might be `&`,
// which needs to be processed by hbs_entity
int at_least_one_char = 0;
hb_char_t c;
hb_proc_char_t c;
while (1) {
c = HBE_CATCH_V(hbu_pipe_peek, pipe);
c = HBE_CATCH_V(hb_proc_peek, pipe);
if (!hbr_unquotedattrval_check(c)) {
if (!hb_rule_unquotedattrval_check(c)) {
return;
}
@ -18,11 +18,11 @@ void hbs_unquoteattrval(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t p
if (c == '&') {
HBE_CATCH_V(hbs_entity, so, pipe);
} else {
HBE_CATCH_V(hbu_pipe_accept, pipe);
HBE_CATCH_V(hb_proc_accept, pipe);
}
}
if (!at_least_one_char) {
HBU_PIPE_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected unquoted attribute value, got `%c` (0x%x)", c);
hb_proc_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected unquoted attribute value, got `%c` (0x%x)", c);
}
}

View File

@ -2,39 +2,39 @@
#include "./quoteattrval.c"
#include "./unquoteattrval.c"
hbs_attr_val_type_t hbs_attr(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe) {
hbs_attr_val_type_t hbs_attr(hbe_err_t *hbe_err, hbu_streamoptions_t so, hb_proc_t pipe) {
hbs_attr_val_type_t rv = 0;
hbu_list_char_t name = hbu_list_char_create();
hb_list_char_t name = hb_list_char_create();
while (1) {
// Char matched by $attrname required at least once
hb_char_t c = HBE_CATCH_F(hbu_pipe_require_predicate, pipe, &hbr_attrname_check, "attribute name");
hb_proc_char_t c = HBE_CATCH_F(hb_proc_require_predicate, pipe, &hb_rule_attrname_check, "attribute name");
if (hbr_ucalpha_check(c)) {
if (hb_rule_ucalpha_check(c)) {
if (!hbu_streamoptions_supressed_error(so, HBE_PARSE_UCASE_ATTR)) {
HBU_PIPE_THROW_F(pipe, HBE_PARSE_UCASE_ATTR, "Uppercase letter in attribute name");
hb_proc_THROW_F(pipe, HBE_PARSE_UCASE_ATTR, "Uppercase letter in attribute name");
}
// Lowercase to normalise when checking against rules and closing tag
hbu_list_char_append(name, c + 32);
hb_list_char_append(name, c + 32);
} else {
hbu_list_char_append(name, c);
hb_list_char_append(name, c);
}
// Don't use hbu_pipe_accept_while_predicate as advanced checks might be needed
hb_char_t n = HBE_CATCH_F(hbu_pipe_peek, pipe);
// Don't use hb_proc_accept_while_predicate as advanced checks might be needed
hb_proc_char_t n = HBE_CATCH_F(hb_proc_peek, pipe);
if (!hbr_attrname_check(n)) {
if (!hb_rule_attrname_check(n)) {
break;
}
}
int collapse_and_trim_whitespace = hbu_list_char_compare_lit(name, "class") == 0 &&
int collapse_and_trim_whitespace = hb_list_char_compare_lit(name, "class") == 0 &&
so->trim_class_attr;
int has_value = HBE_CATCH_F(hbu_pipe_accept_if, pipe, '=');
int has_value = HBE_CATCH_F(hb_proc_accept_if, pipe, '=');
if (has_value) {
hb_char_t next = HBE_CATCH_F(hbu_pipe_peek, pipe);
if (hbr_attrvalquote_check(next)) {
hb_proc_char_t next = HBE_CATCH_F(hb_proc_peek, pipe);
if (hb_rule_attrvalquote_check(next)) {
// Quoted attribute value
hbs_attr_val_type_t type = HBE_CATCH_F(hbs_quoteattrval, so, pipe, collapse_and_trim_whitespace);
// Don't inline as HBE_CATCH_F adds auxiliary statements
@ -42,7 +42,7 @@ hbs_attr_val_type_t hbs_attr(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pip
}
if (!hbu_streamoptions_supressed_error(so, HBE_PARSE_UNQUOTED_ATTR)) {
HBU_PIPE_THROW_F(pipe, HBE_PARSE_UNQUOTED_ATTR, "Unquoted attribute value");
hb_proc_THROW_F(pipe, HBE_PARSE_UNQUOTED_ATTR, "Unquoted attribute value");
}
// Unquoted attribute value
HBE_CATCH_F(hbs_unquoteattrval, so, pipe);
@ -50,7 +50,7 @@ hbs_attr_val_type_t hbs_attr(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pip
}
finally:
hbu_list_char_destroy(name);
hb_list_char_destroy(name);
name = NULL;
return rv;
}

14
src/hb-unit-bang.c Normal file
View File

@ -0,0 +1,14 @@
void hbs_bang(hbe_err_t *hbe_err, hb_proc_t pipe) {
HBE_CATCH_V(hb_proc_require_match, pipe, "<!");
while (1) {
hb_proc_char_t next = HBE_CATCH_V(hb_proc_peek, pipe);
if (next == '>') {
break;
}
HBE_CATCH_V(hb_proc_accept, pipe);
}
HBE_CATCH_V(hb_proc_accept, pipe);
}

16
src/hb-unit-comment.c Normal file
View File

@ -0,0 +1,16 @@
void hbs_comment(hbe_err_t *hbe_err, hbu_streamoptions_t so, hb_proc_t pipe) {
hb_proc_toggle_output_mask(pipe, so->remove_comments);
HBE_CATCH_V(hb_proc_require_match, pipe, "<!--");
while (1) {
int end = HBE_CATCH_V(hb_proc_accept_if_matches, pipe, "-->");
if (end) {
break;
}
HBE_CATCH_V(hb_proc_accept, pipe);
}
hb_proc_toggle_output_mask(pipe, 0);
}

View File

@ -1,5 +1,5 @@
// Declare first before tag.c, as tag.c depends on it
void hbs_content(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe, hb_char_t *parent);
void hbs_content(hbe_err_t *hbe_err, hbu_streamoptions_t so, hb_proc_t pipe, hb_proc_char_t *parent);
#include "../tag/tag.c"
#include "../bang/bang.c"
@ -19,20 +19,20 @@ static int _hbs_content_state_is_comment_bang_or_opening_tag(int state) {
state == HBS_CONTENT_STATE_OPENING_TAG;
}
static int _hbs_content_get_next_state(hbe_err_t *hbe_err, hbu_pipe_t pipe, hb_eod_char_t c) {
int end_tag = HBE_CATCH(hbu_pipe_matches, pipe, "</");
static int _hbs_content_get_next_state(hbe_err_t *hbe_err, hb_proc_t pipe, hb_eod_char_t c) {
int end_tag = HBE_CATCH(hb_proc_matches, pipe, "</");
if (c == HB_EOD || end_tag) {
// End of content
return HBS_CONTENT_STATE_END;
}
int comment = HBE_CATCH(hbu_pipe_matches, pipe, "<!--");
int comment = HBE_CATCH(hb_proc_matches, pipe, "<!--");
if (comment) {
// Comment
return HBS_CONTENT_STATE_COMMENT;
}
int bang = HBE_CATCH(hbu_pipe_matches, pipe, "<!");
int bang = HBE_CATCH(hb_proc_matches, pipe, "<!");
if (bang) {
// Bang
// NOTE: Check after comment
@ -57,7 +57,7 @@ static int _hbs_content_get_next_state(hbe_err_t *hbe_err, hbu_pipe_t pipe, hb_e
}
// $parent can be NULL for top-level content
void hbs_content(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe, hb_char_t *parent) {
void hbs_content(hbe_err_t *hbe_err, hbu_streamoptions_t so, hb_proc_t pipe, hb_proc_char_t *parent) {
int is_first_char = 1;
// Set to 1 when $whitespace is instantiated when $is_first_char is 1
int whitespace_buffer_started_at_beginning = 0;
@ -71,21 +71,21 @@ void hbs_content(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe, hb
int should_trim_whitespace = !hbu_streamoptions_in_tags_list(so->ex_trim_whitespace, parent);
int should_buffer_whitespace = should_collapse_whitespace || should_destroy_whole_whitespace || should_trim_whitespace;
hbu_list_char_t whitespace = NULL;
hb_list_char_t whitespace = NULL;
while (1) {
hb_eod_char_t c = HBE_CATCH_F(hbu_pipe_peek_eoi, pipe);
hb_eod_char_t c = HBE_CATCH_F(hb_proc_peek_eoi, pipe);
int next_state = HBE_CATCH_F(_hbs_content_get_next_state, pipe, c);
if (next_state == HBS_CONTENT_STATE_TEXT && hbr_whitespace_check(c) && should_buffer_whitespace) {
if (next_state == HBS_CONTENT_STATE_TEXT && hb_rule_whitespace_check(c) && should_buffer_whitespace) {
// Next character is whitespace and whitespace should be buffered
if (whitespace == NULL) {
whitespace = hbu_list_char_create();
whitespace = hb_list_char_create();
whitespace_buffer_started_at_beginning = is_first_char;
whitespace_buffer_started_after_right_chevron = returned_from_comment_bang_or_tag;
}
hbu_list_char_append(whitespace, c);
HBE_CATCH_F(hbu_pipe_skip, pipe);
hb_list_char_append(whitespace, c);
HBE_CATCH_F(hb_proc_skip, pipe);
} else {
if (whitespace != NULL) {
@ -101,17 +101,17 @@ void hbs_content(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe, hb
// Do nothing
} else if (should_collapse_whitespace) {
HBE_CATCH_F(hbu_pipe_write, pipe, ' ');
HBE_CATCH_F(hb_proc_write, pipe, ' ');
}
hbu_list_char_destroy(whitespace);
hb_list_char_destroy(whitespace);
whitespace = NULL;
whitespace_buffer_started_at_beginning = 0;
}
switch (next_state) {
case HBS_CONTENT_STATE_TEXT:
HBE_CATCH_F(hbu_pipe_accept, pipe);
HBE_CATCH_F(hb_proc_accept, pipe);
break;
case HBS_CONTENT_STATE_COMMENT:
@ -145,7 +145,7 @@ void hbs_content(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe, hb
finally:
if (whitespace != NULL) {
hbu_list_char_destroy(whitespace);
hb_list_char_destroy(whitespace);
whitespace = NULL;
}
}

View File

@ -0,0 +1,126 @@
static void _hbs_script_slcomment(hbe_err_t *hbe_err, hb_proc_t pipe) {
HBE_CATCH_V(hb_proc_require_match, pipe, "//");
// Comment can end at closing </script>
// NOTE: Closing tag must not contain whitespace
while (1) {
int line_term = HBE_CATCH_V(hb_proc_accept_if_matches_line_terminator, pipe);
if (line_term) {
break;
}
int end_tag = HBE_CATCH_V(hb_proc_matches_i, pipe, "</script>");
if (end_tag) {
break;
}
HBE_CATCH_V(hb_proc_accept, pipe);
}
}
static void _hbs_script_mlcomment(hbe_err_t *hbe_err, hb_proc_t pipe) {
HBE_CATCH_V(hb_proc_require_match, pipe, "/*");
// Comment can end at closing </script>
// NOTE: Closing tag must not contain whitespace
while (1) {
int end = HBE_CATCH_V(hb_proc_accept_if_matches, pipe, "*/");
if (end) {
break;
}
int end_tag = HBE_CATCH_V(hb_proc_matches_i, pipe, "</script>");
if (end_tag) {
break;
}
HBE_CATCH_V(hb_proc_accept, pipe);
}
}
static void _hbs_script_string(hbe_err_t *hbe_err, hb_proc_t pipe) {
hb_proc_char_t delim = HBE_CATCH_V(hb_proc_accept, pipe);
if (delim != '"' && delim != '\'') {
hb_proc_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected JavaScript string delimiter");
}
int escaping = 0;
while (1) {
hb_proc_char_t c = HBE_CATCH_V(hb_proc_accept, pipe);
if (c == '\\') {
escaping ^= 1;
continue;
}
if (c == delim && !escaping) {
break;
}
int line_term = HBE_CATCH_V(hb_proc_accept_if_matches_line_terminator, pipe);
if (line_term) {
if (!escaping) {
hb_proc_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Unterminated JavaScript string");
}
}
escaping = 0;
}
}
static void _hbs_script_template(hbe_err_t *hbe_err, hb_proc_t pipe) {
HBE_CATCH_V(hb_proc_require_match, pipe, "`");
int escaping = 0;
while (1) {
hb_proc_char_t c = HBE_CATCH_V(hb_proc_accept, pipe);
if (c == '\\') {
escaping ^= 1;
continue;
}
if (c == '`' && !escaping) {
break;
}
escaping = 0;
}
}
void hbs_script(hbe_err_t *hbe_err, hb_proc_t pipe) {
while (1) {
int end = HBE_CATCH_V(hb_proc_matches, pipe, "</");
if (end) {
break;
}
int sl_comment = HBE_CATCH_V(hb_proc_matches, pipe, "//");
if (sl_comment) {
HBE_CATCH_V(_hbs_script_slcomment, pipe);
continue;
}
int ml_comment = HBE_CATCH_V(hb_proc_matches, pipe, "/*");
if (ml_comment) {
HBE_CATCH_V(_hbs_script_mlcomment, pipe);
continue;
}
hb_proc_char_t next = HBE_CATCH_V(hb_proc_peek, pipe);
if (next == '"' || next == '\'') {
HBE_CATCH_V(_hbs_script_string, pipe);
continue;
}
if (next == '`') {
HBE_CATCH_V(_hbs_script_template, pipe);
continue;
}
HBE_CATCH_V(hb_proc_accept, pipe);
}
}

View File

@ -0,0 +1,67 @@
static void _hbs_style_comment(hbe_err_t *hbe_err, hb_proc_t pipe) {
HBE_CATCH_V(hb_proc_require_match, pipe, "/*");
// Unlike script tags, style comments do NOT end at closing tag
while (1) {
int is_end = HBE_CATCH_V(hb_proc_accept_if_matches, pipe, "*/");
if (is_end) {
break;
}
HBE_CATCH_V(hb_proc_accept, pipe);
}
}
static void _hbs_style_string(hbe_err_t *hbe_err, hb_proc_t pipe) {
hb_proc_char_t delim = HBE_CATCH_V(hb_proc_accept, pipe);
if (delim != '"' && delim != '\'') {
hb_proc_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected CSS string delimiter");
}
int escaping = 0;
while (1) {
hb_proc_char_t c = HBE_CATCH_V(hb_proc_accept, pipe);
if (c == '\\') {
escaping ^= 1;
continue;
}
if (c == delim && !escaping) {
break;
}
int line_term = HBE_CATCH_V(hb_proc_accept_if_matches_line_terminator, pipe);
if (line_term) {
if (!escaping) {
hb_proc_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Unterminated CSS string");
}
}
escaping = 0;
}
}
void hbs_style(hbe_err_t *hbe_err, hb_proc_t pipe) {
while (1) {
int end = HBE_CATCH_V(hb_proc_matches, pipe, "</");
if (end) {
break;
}
int is_comment = HBE_CATCH_V(hb_proc_matches, pipe, "/*");
if (is_comment) {
HBE_CATCH_V(_hbs_style_comment, pipe);
continue;
}
hb_proc_char_t next = HBE_CATCH_V(hb_proc_peek, pipe);
if (next == '"' || next == '\'') {
HBE_CATCH_V(_hbs_style_string, pipe);
continue;
}
HBE_CATCH_V(hb_proc_accept, pipe);
}
}

View File

@ -11,54 +11,54 @@ static void _hbs_entity_interr_unknown_entity(hbe_err_t *hbe_err, int type) {
HBE_THROW_V(HBE_INTERR_UNKNOWN_ENTITY_TYPE, "INTERR %d is not a known entity type", type);
}
static void _hbs_entity_write_literal(hbe_err_t *hbe_err, hbu_pipe_t pipe, int type, hbu_list_char_t entity_raw, int consumed_semicolon) {
HBE_CATCH_V(hbu_pipe_write, pipe, '&');
static void _hbs_entity_write_literal(hbe_err_t *hbe_err, hb_proc_t pipe, int type, hb_list_char_t entity_raw, int consumed_semicolon) {
HBE_CATCH_V(hb_proc_write, pipe, '&');
if (type == HBS_ENTITY_TYPE_HEXADECIMAL || type == HBS_ENTITY_TYPE_DECIMAL) {
HBE_CATCH_V(hbu_pipe_write, pipe, '#');
HBE_CATCH_V(hb_proc_write, pipe, '#');
if (type == HBS_ENTITY_TYPE_HEXADECIMAL) {
HBE_CATCH_V(hbu_pipe_write, pipe, 'x');
HBE_CATCH_V(hb_proc_write, pipe, 'x');
}
}
if (entity_raw != NULL) {
HBE_CATCH_V(hbu_pipe_write_buffer, pipe, entity_raw);
HBE_CATCH_V(hb_proc_write_buffer, pipe, entity_raw);
}
if (consumed_semicolon) {
HBE_CATCH_V(hbu_pipe_write, pipe, ';');
HBE_CATCH_V(hb_proc_write, pipe, ';');
}
}
static void _hbs_entity_syntax_error(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe, int type, hbu_list_char_t entity_raw, int consumed_semicolon, hbe_errcode_t errcode, const char *reason) {
static void _hbs_entity_syntax_error(hbe_err_t *hbe_err, hbu_streamoptions_t so, hb_proc_t pipe, int type, hb_list_char_t entity_raw, int consumed_semicolon, hb_error_t errcode, const char *reason) {
if (hbu_streamoptions_supressed_error(so, errcode)) {
HBE_CATCH_V(_hbs_entity_write_literal, pipe, type, entity_raw, consumed_semicolon);
return;
}
HBU_PIPE_THROW_V(pipe, errcode, reason);
hb_proc_THROW_V(pipe, errcode, reason);
}
// NOTE: Return 0 if syntax error but suppressed
static int _hbs_entity_process_prefix(hbe_err_t *hbe_err, hbu_pipe_t pipe, hbu_streamoptions_t so) {
hb_char_t c = HBE_CATCH(hbu_pipe_peek, pipe);
static int _hbs_entity_process_prefix(hbe_err_t *hbe_err, hb_proc_t pipe, hbu_streamoptions_t so) {
hb_proc_char_t c = HBE_CATCH(hb_proc_peek, pipe);
if (hbr_lcalpha_check(c) || hbr_ucalpha_check(c)) {
if (hb_rule_lcalpha_check(c) || hb_rule_ucalpha_check(c)) {
// Name-based entity
return HBS_ENTITY_TYPE_NAME;
}
hb_eod_char_t c2 = HBE_CATCH(hbu_pipe_peek_eoi_offset, pipe, 2);
hb_eod_char_t c2 = HBE_CATCH(hb_proc_peek_eoi_offset, pipe, 2);
if (c == '#' && c2 == 'x') {
// Hexadecimal-based entity
// NOTE: Check before decimal-based
HBE_CATCH(hbu_pipe_skip_amount, pipe, 2);
HBE_CATCH(hb_proc_skip_amount, pipe, 2);
return HBS_ENTITY_TYPE_HEXADECIMAL;
}
if (c == '#') {
// Decimal-based entity
HBE_CATCH(hbu_pipe_skip, pipe);
HBE_CATCH(hb_proc_skip, pipe);
return HBS_ENTITY_TYPE_DECIMAL;
}
@ -66,17 +66,17 @@ static int _hbs_entity_process_prefix(hbe_err_t *hbe_err, hbu_pipe_t pipe, hbu_s
HBE_PASS(_hbs_entity_syntax_error, so, pipe, -1, NULL, 0, HBE_PARSE_MALFORMED_ENTITY, "Invalid character after ampersand");
}
void hbs_entity(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe) {
hbu_list_char_t entity_raw = NULL;
void hbs_entity(hbe_err_t *hbe_err, hbu_streamoptions_t so, hb_proc_t pipe) {
hb_list_char_t entity_raw = NULL;
HBE_CATCH_F(hbu_pipe_require_skip, pipe, '&');
HBE_CATCH_F(hb_proc_require_skip, pipe, '&');
// Quickly check and short circuit if BARE_AMPERSAND is suppressed
// and next character is whitespace
if (hbu_streamoptions_supressed_error(so, HBE_PARSE_BARE_AMPERSAND)) {
hb_eod_char_t next = HBE_CATCH_F(hbu_pipe_peek_eoi, pipe);
if (hbr_whitespace_check(next)) {
HBE_CATCH_F(hbu_pipe_write, pipe, '&');
hb_eod_char_t next = HBE_CATCH_F(hb_proc_peek_eoi, pipe);
if (hb_rule_whitespace_check(next)) {
HBE_CATCH_F(hb_proc_write, pipe, '&');
return;
}
}
@ -87,11 +87,11 @@ void hbs_entity(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe) {
goto finally;
}
entity_raw = hbu_list_char_create_size(0, HBS_ENTITY_MAX_ENTITY_LENGTH + 1);
entity_raw = hb_list_char_create_size(0, HBS_ENTITY_MAX_ENTITY_LENGTH + 1);
int under_max = 0;
for (int i = 0; i < HBS_ENTITY_MAX_ENTITY_LENGTH; i++) {
hb_char_t e = HBE_CATCH_F(hbu_pipe_skip, pipe);
hb_proc_char_t e = HBE_CATCH_F(hb_proc_skip, pipe);
if (e == ';') {
under_max = 1;
@ -102,15 +102,15 @@ void hbs_entity(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe) {
switch (type) {
case HBS_ENTITY_TYPE_NAME:
well_formed = hbr_lcalpha_check(e) || hbr_ucalpha_check(e);
well_formed = hb_rule_lcalpha_check(e) || hb_rule_ucalpha_check(e);
break;
case HBS_ENTITY_TYPE_DECIMAL:
well_formed = hbr_digit_check(e);
well_formed = hb_rule_digit_check(e);
break;
case HBS_ENTITY_TYPE_HEXADECIMAL:
well_formed = hbr_hex_check(e);
well_formed = hb_rule_hex_check(e);
break;
default:
@ -121,7 +121,7 @@ void hbs_entity(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe) {
HBE_PASS_F(_hbs_entity_syntax_error, so, pipe, type, entity_raw, 0, HBE_PARSE_MALFORMED_ENTITY, "Characters after ampersand don't form entity");
}
hbu_list_char_append(entity_raw, e);
hb_list_char_append(entity_raw, e);
}
if (!under_max) {
@ -130,14 +130,14 @@ void hbs_entity(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe) {
}
int valid = 1;
hb_char_t *entity_raw_u = hbu_list_char_underlying(entity_raw);
hb_proc_char_t *entity_raw_u = hb_list_char_underlying(entity_raw);
uintmax_t code_point;
switch (type) {
case HBS_ENTITY_TYPE_NAME:
valid = hbr_entityrefs_check(entity_raw_u);
valid = hb_rule_entity_references_check(entity_raw_u);
if (valid && so->decode_entities) {
HBE_CATCH_F(hbu_pipe_write, pipe, hbr_entityrefs_get(entity_raw_u));
HBE_CATCH_F(hb_proc_write, pipe, hb_rule_entity_references_get(entity_raw_u));
}
break;
@ -146,7 +146,7 @@ void hbs_entity(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe) {
code_point = strtoumax((char *) entity_raw_u, NULL, (type == HBS_ENTITY_TYPE_DECIMAL) ? 10 : 16);
valid = errno == 0 && code_point <= 0x10FFFF;
if (valid && so->decode_entities) {
valid = HBE_CATCH_F(hbu_pipe_write_unicode, pipe, code_point);
valid = HBE_CATCH_F(hb_proc_write_unicode, pipe, code_point);
}
break;
@ -164,7 +164,7 @@ void hbs_entity(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe) {
finally:
if (entity_raw != NULL) {
hbu_list_char_destroy(entity_raw);
hb_list_char_destroy(entity_raw);
entity_raw = NULL;
}
}

34
src/hb-unit-tag-name.c Normal file
View File

@ -0,0 +1,34 @@
hb_list_char_t hbs_tagname(hbe_err_t *hbe_err, hbu_streamoptions_t so, hb_proc_t pipe) {
hb_list_char_t name = hb_list_char_create();
while (1) {
hb_proc_char_t c = HBE_CATCH_F(hb_proc_peek, pipe);
if (!hb_rule_tagname_check(c)) {
break;
}
if (hb_rule_ucalpha_check(c)) {
if (!hbu_streamoptions_supressed_error(so, HBE_PARSE_UCASE_TAG)) {
hb_proc_THROW_F(pipe, HBE_PARSE_UCASE_TAG, "Uppercase character in tag");
}
// Lowercase to normalise when checking against rules and closing tag
hb_list_char_append(name, c + 32);
} else {
hb_list_char_append(name, c);
}
HBE_CATCH_F(hb_proc_accept, pipe);
}
if (!hbu_streamoptions_supressed_error(so, HBE_PARSE_NONSTANDARD_TAG) && !hb_rule_tags_check(hb_list_char_underlying(name))) {
hb_proc_THROW_F(pipe, HBE_PARSE_NONSTANDARD_TAG, "Non-standard tag");
}
finally:
if (*hbe_err != NULL) {
hb_list_char_destroy(name);
name = NULL;
}
return name;
}

102
src/hb-unit-tag.c Normal file
View File

@ -0,0 +1,102 @@
// Declare first before content.c, as content.c depends on it
void hbs_tag(hbe_err_t *hbe_err, hbu_streamoptions_t so, hb_proc_t pipe, hb_proc_char_t *parent);
#include "./tagname.c"
#include "../attr/attr.c"
#include "../content/script.c"
#include "../content/style.c"
#include "../content/html.c"
// $parent could be NULL
void hbs_tag(hbe_err_t *hbe_err, hbu_streamoptions_t so, hb_proc_t pipe, hb_proc_char_t *parent) {
hb_list_char_t opening_name = NULL;
int self_closing = 0;
HBE_CATCH_F(hb_proc_require, pipe, '<');
opening_name = HBE_CATCH_F(hbs_tagname, so, pipe);
int last_attr_type = -1;
while (1) {
// At the beginning of this loop, the last parsed unit was either the tag name
// or an attribute (including its value, if it had one)
size_t ws_accepted;
if (so->remove_tag_whitespace) {
ws_accepted = HBE_CATCH_F(hb_proc_skip_while_predicate, pipe, &hb_rule_whitespace_check);
} else {
ws_accepted = HBE_CATCH_F(hb_proc_accept_while_predicate, pipe, &hb_rule_whitespace_check);
}
int end_of_tag = HBE_CATCH_F(hb_proc_accept_if, pipe, '>');
if (end_of_tag) {
break;
}
self_closing = HBE_CATCH_F(hb_proc_accept_if_matches, pipe, "/>");
if (self_closing) {
if (!hbu_streamoptions_supressed_error(so, HBE_PARSE_SELF_CLOSING_TAG)) {
hb_proc_THROW_F(pipe, HBE_PARSE_SELF_CLOSING_TAG, "Self-closing tag");
}
break;
}
// HBE_PARSE_NO_SPACE_BEFORE_ATTR is not suppressable as then there would be difficulty
// in determining what is the end of a tag/attribute name/attribute value
if (!ws_accepted) {
hb_proc_THROW_F(pipe, HBE_PARSE_NO_SPACE_BEFORE_ATTR, "No whitespace before attribute");
}
if (so->remove_tag_whitespace) {
if (last_attr_type != HBS_ATTR_QUOTED) {
HBE_CATCH_F(hb_proc_write, pipe, ' ');
}
}
last_attr_type = HBE_CATCH_F(hbs_attr, so, pipe);
}
hb_proc_char_t *tag_name = hb_list_char_underlying(opening_name);
// Non-standard tag checking is done in hbs_tagname
if (parent != NULL && (
!hb_rule_whitelistparents_allowed(tag_name, parent) ||
!hb_rule_whitelistchildren_allowed(parent, tag_name) ||
!hb_rule_blacklistparents_allowed(tag_name, parent) ||
!hb_rule_blacklistchildren_allowed(parent, tag_name))) {
hb_proc_THROW_F(pipe, HBE_PARSE_ILLEGAL_CHILD, "Tag can't be a child there");
}
// Self-closing or void tag
if (self_closing || hb_rule_voidtags_check(tag_name)) {
goto finally;
}
if (hb_list_char_compare_lit(opening_name, "script") == 0) {
// Script tag
HBE_CATCH_F(hbs_script, pipe);
} else if (hb_list_char_compare_lit(opening_name, "style") == 0) {
// Style tag
HBE_CATCH_F(hbs_style, pipe);
} else {
// Content
HBE_CATCH_F(hbs_content, so, pipe, tag_name);
}
// Closing tag for non-void
HBE_CATCH_F(hb_proc_require, pipe, '<');
HBE_CATCH_F(hb_proc_require, pipe, '/');
hb_list_char_t closing_name = HBE_CATCH_F(hbs_tagname, so, pipe);
HBE_CATCH_F(hb_proc_require, pipe, '>');
if (!hb_list_char_equal(opening_name, closing_name)) {
hb_proc_THROW_F(pipe, HBE_PARSE_UNCLOSED_TAG, "Tag not closed (expected `%s` closing tag, got `%s`)", tag_name, hb_list_char_underlying(closing_name));
}
finally:
if (opening_name) {
hb_list_char_destroy(opening_name);
opening_name = NULL;
}
return;
}

0
src/hb-unit.h Normal file
View File

6
src/hyperbuild.c Normal file
View File

@ -0,0 +1,6 @@
void hb_init(void) {
// Set up rules
hb_rule_init();
// Set up config
hb_config_init();
}

View File

@ -12,7 +12,7 @@
#include "./char/unquotedattrval.c"
#include "./char/whitespace.c"
#include "./entity/entityrefs.c"
#include "./entity/entity_references.c"
#include "./relation/blacklistchildren.c"
#include "./relation/blacklistparents.c"
@ -33,48 +33,48 @@
#include "./tag/svgtags.c"
#include "./tag/tags.c"
void hbr_init(void) {
void hb_rule_init(void) {
// Core
hbr_c0_init();
hbr_digit_init();
hbr_hex_init();
hbr_ucalpha_init();
hbr_lcalpha_init();
hbr_whitespace_init();
hb_rule_c0_init();
hb_rule_digit_init();
hb_rule_hex_init();
hb_rule_ucalpha_init();
hb_rule_lcalpha_init();
hb_rule_whitespace_init();
// Identifiers
hbr_tagname_init();
hbr_attrname_init();
hb_rule_tagname_init();
hb_rule_attrname_init();
// Values
hbr_attrvalquote_init();
hbr_unquotedattrval_init();
hbr_entityrefs_init();
hb_rule_attrvalquote_init();
hb_rule_unquotedattrval_init();
hb_rule_entity_references_init();
// Specification tag categories
hbr_headingtags_init();
hbr_mediatags_init();
hbr_sectioningtags_init();
hb_rule_headingtags_init();
hb_rule_mediatags_init();
hb_rule_sectioningtags_init();
hbr_voidtags_init();
hbr_wsstags_init();
hb_rule_voidtags_init();
hb_rule_wsstags_init();
hbr_htmltags_init();
hbr_svgtags_init();
hbr_tags_init();
hb_rule_htmltags_init();
hb_rule_svgtags_init();
hb_rule_tags_init();
// Hyperbuild tag categories
hbr_contentfirsttags_init();
hbr_contenttags_init();
hbr_formattingtags_init();
hbr_layouttags_init();
hbr_specifictags_init();
hb_rule_contentfirsttags_init();
hb_rule_contenttags_init();
hb_rule_formattingtags_init();
hb_rule_layouttags_init();
hb_rule_specifictags_init();
// Relations
hbr_whitelistparents_init();
hbr_blacklistparents_init();
hbr_whitelistchildren_init();
hbr_blacklistchildren_init();
hb_rule_whitelistparents_init();
hb_rule_blacklistparents_init();
hb_rule_whitelistchildren_init();
hb_rule_blacklistchildren_init();
}
#endif // _HDR_HYPERBUILD_RULE_INIT

View File

@ -1,19 +1,19 @@
#include "./c0.c"
static nh_set_int32_t hbr_attrname_blacklist;
static nh_set_int32_t hb_rule_attrname_blacklist;
void hbr_attrname_init(void) {
hbr_attrname_blacklist = nh_set_int32_create();
hbr_c0_add_elems(hbr_attrname_blacklist);
nh_set_int32_add(hbr_attrname_blacklist, ' ');
nh_set_int32_add(hbr_attrname_blacklist, '"');
nh_set_int32_add(hbr_attrname_blacklist, '\'');
nh_set_int32_add(hbr_attrname_blacklist, '>');
nh_set_int32_add(hbr_attrname_blacklist, '/');
nh_set_int32_add(hbr_attrname_blacklist, '=');
void hb_rule_attrname_init(void) {
hb_rule_attrname_blacklist = nh_set_int32_create();
hb_rule_c0_add_elems(hb_rule_attrname_blacklist);
nh_set_int32_add(hb_rule_attrname_blacklist, ' ');
nh_set_int32_add(hb_rule_attrname_blacklist, '"');
nh_set_int32_add(hb_rule_attrname_blacklist, '\'');
nh_set_int32_add(hb_rule_attrname_blacklist, '>');
nh_set_int32_add(hb_rule_attrname_blacklist, '/');
nh_set_int32_add(hb_rule_attrname_blacklist, '=');
// NOTE: Unicode noncharacters not tested (https://html.spec.whatwg.org/multipage/syntax.html#syntax-attribute-name)
}
int hbr_attrname_check(hb_char_t c) {
return !nh_set_int32_has(hbr_attrname_blacklist, c);
int hb_rule_attrname_check(hb_proc_char_t c) {
return !nh_set_int32_has(hb_rule_attrname_blacklist, c);
}

View File

@ -1,13 +1,13 @@
#include "./c0.c"
static nh_set_int32_t hbr_attrvalquote_set;
static nh_set_int32_t hb_rule_attrvalquote_set;
void hbr_attrvalquote_init(void) {
hbr_attrvalquote_set = nh_set_int32_create();
nh_set_int32_add(hbr_attrvalquote_set, '\'');
nh_set_int32_add(hbr_attrvalquote_set, '"');
void hb_rule_attrvalquote_init(void) {
hb_rule_attrvalquote_set = nh_set_int32_create();
nh_set_int32_add(hb_rule_attrvalquote_set, '\'');
nh_set_int32_add(hb_rule_attrvalquote_set, '"');
}
int hbr_attrvalquote_check(hb_char_t c) {
return nh_set_int32_has(hbr_attrvalquote_set, c);
int hb_rule_attrvalquote_check(hb_proc_char_t c) {
return nh_set_int32_has(hb_rule_attrvalquote_set, c);
}

View File

@ -1,16 +1,16 @@
static nh_set_int32_t hbr_c0_set;
static nh_set_int32_t hb_rule_c0_set;
void hbr_c0_add_elems(nh_set_int32_t set) {
void hb_rule_c0_add_elems(nh_set_int32_t set) {
for (char c = 0x0; c <= 0x1F; c++) {
nh_set_int32_add(set, c);
}
}
void hbr_c0_init(void) {
hbr_c0_set = nh_set_int32_create();
hbr_c0_add_elems(hbr_c0_set);
void hb_rule_c0_init(void) {
hb_rule_c0_set = nh_set_int32_create();
hb_rule_c0_add_elems(hb_rule_c0_set);
}
int hbr_c0_check(hb_char_t c) {
return nh_set_int32_has(hbr_c0_set, c);
int hb_rule_c0_check(hb_proc_char_t c) {
return nh_set_int32_has(hb_rule_c0_set, c);
}

View File

@ -1,16 +1,16 @@
static nh_set_int32_t hbr_digit_set;
static nh_set_int32_t hb_rule_digit_set;
void hbr_digit_add_elems(nh_set_int32_t set) {
void hb_rule_digit_add_elems(nh_set_int32_t set) {
for (char c = 0x30; c <= 0x39; c++) {
nh_set_int32_add(set, c);
}
}
void hbr_digit_init(void) {
hbr_digit_set = nh_set_int32_create();
hbr_digit_add_elems(hbr_digit_set);
void hb_rule_digit_init(void) {
hb_rule_digit_set = nh_set_int32_create();
hb_rule_digit_add_elems(hb_rule_digit_set);
}
int hbr_digit_check(hb_char_t c) {
return nh_set_int32_has(hbr_digit_set, c);
int hb_rule_digit_check(hb_proc_char_t c) {
return nh_set_int32_has(hb_rule_digit_set, c);
}

View File

@ -1,6 +1,6 @@
static nh_set_int32_t hbr_hex_set;
static nh_set_int32_t hb_rule_hex_set;
void hbr_hex_add_elems(nh_set_int32_t set) {
void hb_rule_hex_add_elems(nh_set_int32_t set) {
for (char c = 0x30; c <= 0x39; c++) { // 0-9
nh_set_int32_add(set, c);
}
@ -12,11 +12,11 @@ void hbr_hex_add_elems(nh_set_int32_t set) {
}
}
void hbr_hex_init(void) {
hbr_hex_set = nh_set_int32_create();
hbr_hex_add_elems(hbr_hex_set);
void hb_rule_hex_init(void) {
hb_rule_hex_set = nh_set_int32_create();
hb_rule_hex_add_elems(hb_rule_hex_set);
}
int hbr_hex_check(hb_char_t c) {
return nh_set_int32_has(hbr_hex_set, c);
int hb_rule_hex_check(hb_proc_char_t c) {
return nh_set_int32_has(hb_rule_hex_set, c);
}

View File

@ -1,16 +1,16 @@
static nh_set_int32_t hbr_lcalpha_set;
static nh_set_int32_t hb_rule_lcalpha_set;
void hbr_lcalpha_add_elems(nh_set_int32_t set) {
void hb_rule_lcalpha_add_elems(nh_set_int32_t set) {
for (char c = 0x61; c <= 0x7A; c++) {
nh_set_int32_add(set, c);
}
}
void hbr_lcalpha_init(void) {
hbr_lcalpha_set = nh_set_int32_create();
hbr_lcalpha_add_elems(hbr_lcalpha_set);
void hb_rule_lcalpha_init(void) {
hb_rule_lcalpha_set = nh_set_int32_create();
hb_rule_lcalpha_add_elems(hb_rule_lcalpha_set);
}
int hbr_lcalpha_check(hb_char_t c) {
return nh_set_int32_has(hbr_lcalpha_set, c);
int hb_rule_lcalpha_check(hb_proc_char_t c) {
return nh_set_int32_has(hb_rule_lcalpha_set, c);
}

View File

@ -2,17 +2,17 @@
#include "./ucalpha.c"
#include "./digit.c"
static nh_set_int32_t hbr_tagname_set;
static nh_set_int32_t hb_rule_tagname_set;
void hbr_tagname_init(void) {
hbr_tagname_set = nh_set_int32_create();
hbr_lcalpha_add_elems(hbr_tagname_set);
hbr_ucalpha_add_elems(hbr_tagname_set);
hbr_digit_add_elems(hbr_tagname_set);
nh_set_int32_add(hbr_tagname_set, ':');
nh_set_int32_add(hbr_tagname_set, '-');
void hb_rule_tagname_init(void) {
hb_rule_tagname_set = nh_set_int32_create();
hb_rule_lcalpha_add_elems(hb_rule_tagname_set);
hb_rule_ucalpha_add_elems(hb_rule_tagname_set);
hb_rule_digit_add_elems(hb_rule_tagname_set);
nh_set_int32_add(hb_rule_tagname_set, ':');
nh_set_int32_add(hb_rule_tagname_set, '-');
}
int hbr_tagname_check(hb_char_t c) {
return nh_set_int32_has(hbr_tagname_set, c);
int hb_rule_tagname_check(hb_proc_char_t c) {
return nh_set_int32_has(hb_rule_tagname_set, c);
}

View File

@ -1,16 +1,16 @@
static nh_set_int32_t hbr_ucalpha_set;
static nh_set_int32_t hb_rule_ucalpha_set;
void hbr_ucalpha_add_elems(nh_set_int32_t set) {
void hb_rule_ucalpha_add_elems(nh_set_int32_t set) {
for (char c = 0x41; c <= 0x5A; c++) {
nh_set_int32_add(set, c);
}
}
void hbr_ucalpha_init(void) {
hbr_ucalpha_set = nh_set_int32_create();
hbr_ucalpha_add_elems(hbr_ucalpha_set);
void hb_rule_ucalpha_init(void) {
hb_rule_ucalpha_set = nh_set_int32_create();
hb_rule_ucalpha_add_elems(hb_rule_ucalpha_set);
}
int hbr_ucalpha_check(hb_char_t c) {
return nh_set_int32_has(hbr_ucalpha_set, c);
int hb_rule_ucalpha_check(hb_proc_char_t c) {
return nh_set_int32_has(hb_rule_ucalpha_set, c);
}

View File

@ -1,18 +1,18 @@
#include "./whitespace.c"
static nh_set_int32_t hbr_unquotedattrval_blacklist;
static nh_set_int32_t hb_rule_unquotedattrval_blacklist;
void hbr_unquotedattrval_init(void) {
hbr_unquotedattrval_blacklist = nh_set_int32_create();
hbr_whitespace_add_elems(hbr_unquotedattrval_blacklist);
nh_set_int32_add(hbr_unquotedattrval_blacklist, '"');
nh_set_int32_add(hbr_unquotedattrval_blacklist, '\'');
nh_set_int32_add(hbr_unquotedattrval_blacklist, '`');
nh_set_int32_add(hbr_unquotedattrval_blacklist, '=');
nh_set_int32_add(hbr_unquotedattrval_blacklist, '<');
nh_set_int32_add(hbr_unquotedattrval_blacklist, '>');
void hb_rule_unquotedattrval_init(void) {
hb_rule_unquotedattrval_blacklist = nh_set_int32_create();
hb_rule_whitespace_add_elems(hb_rule_unquotedattrval_blacklist);
nh_set_int32_add(hb_rule_unquotedattrval_blacklist, '"');
nh_set_int32_add(hb_rule_unquotedattrval_blacklist, '\'');
nh_set_int32_add(hb_rule_unquotedattrval_blacklist, '`');
nh_set_int32_add(hb_rule_unquotedattrval_blacklist, '=');
nh_set_int32_add(hb_rule_unquotedattrval_blacklist, '<');
nh_set_int32_add(hb_rule_unquotedattrval_blacklist, '>');
}
int hbr_unquotedattrval_check(hb_char_t c) {
return !nh_set_int32_has(hbr_unquotedattrval_blacklist, c);
int hb_rule_unquotedattrval_check(hb_proc_char_t c) {
return !nh_set_int32_has(hb_rule_unquotedattrval_blacklist, c);
}

View File

@ -1,6 +1,6 @@
static nh_set_int32_t hbr_whitespace_set;
static nh_set_int32_t hb_rule_whitespace_set;
void hbr_whitespace_add_elems(nh_set_int32_t set) {
void hb_rule_whitespace_add_elems(nh_set_int32_t set) {
nh_set_int32_add(set, 0x09); // TAB
nh_set_int32_add(set, 0x0A); // LF
nh_set_int32_add(set, 0x0C); // FF
@ -8,11 +8,11 @@ void hbr_whitespace_add_elems(nh_set_int32_t set) {
nh_set_int32_add(set, 0x20); // SPACE
}
void hbr_whitespace_init(void) {
hbr_whitespace_set = nh_set_int32_create();
hbr_whitespace_add_elems(hbr_whitespace_set);
void hb_rule_whitespace_init(void) {
hb_rule_whitespace_set = nh_set_int32_create();
hb_rule_whitespace_add_elems(hb_rule_whitespace_set);
}
int hbr_whitespace_check(hb_char_t c) {
return nh_set_int32_has(hbr_whitespace_set, c);
int hb_rule_whitespace_check(hb_proc_char_t c) {
return nh_set_int32_has(hb_rule_whitespace_set, c);
}

File diff suppressed because it is too large Load Diff

View File

@ -2,80 +2,80 @@
#include "../tag/mediatags.c"
#include "../tag/sectioningtags.c"
static nh_map_str_strset_t hbr_blacklistchildren_map;
static nh_map_str_strset_t hb_rule_blacklistchildren_map;
void hbr_blacklistchildren_init(void) {
hbr_blacklistchildren_map = nh_map_str_strset_create();
void hb_rule_blacklistchildren_init(void) {
hb_rule_blacklistchildren_map = nh_map_str_strset_create();
// <address>
nh_set_str_t address = nh_set_str_create();
hbr_headingtags_add_elems(address);
hbr_sectioningtags_add_elems(address);
hb_rule_headingtags_add_elems(address);
hb_rule_sectioningtags_add_elems(address);
nh_set_str_add(address, "address");
nh_set_str_add(address, "header");
nh_set_str_add(address, "footer");
nh_map_str_strset_set(hbr_blacklistchildren_map, "address", address);
nh_map_str_strset_set(hb_rule_blacklistchildren_map, "address", address);
// <audio>
nh_set_str_t audio = nh_set_str_create();
hbr_mediatags_add_elems(audio);
nh_map_str_strset_set(hbr_blacklistchildren_map, "audio", audio);
hb_rule_mediatags_add_elems(audio);
nh_map_str_strset_set(hb_rule_blacklistchildren_map, "audio", audio);
// <dfn>
nh_set_str_t dfn = nh_set_str_create();
nh_set_str_add(dfn, "dfn");
nh_map_str_strset_set(hbr_blacklistchildren_map, "dfn", dfn);
nh_map_str_strset_set(hb_rule_blacklistchildren_map, "dfn", dfn);
// <dt>
nh_set_str_t dt = nh_set_str_create();
hbr_headingtags_add_elems(dt);
hbr_sectioningtags_add_elems(dt);
hb_rule_headingtags_add_elems(dt);
hb_rule_sectioningtags_add_elems(dt);
nh_set_str_add(dt, "header");
nh_set_str_add(dt, "footer");
nh_map_str_strset_set(hbr_blacklistchildren_map, "dt", dt);
nh_map_str_strset_set(hb_rule_blacklistchildren_map, "dt", dt);
// <footer>
nh_set_str_t footer = nh_set_str_create();
nh_set_str_add(footer, "header");
nh_set_str_add(footer, "footer");
nh_map_str_strset_set(hbr_blacklistchildren_map, "footer", footer);
nh_map_str_strset_set(hb_rule_blacklistchildren_map, "footer", footer);
// <form>
nh_set_str_t form = nh_set_str_create();
nh_set_str_add(form, "form");
nh_map_str_strset_set(hbr_blacklistchildren_map, "form", form);
nh_map_str_strset_set(hb_rule_blacklistchildren_map, "form", form);
// <header>
nh_set_str_t header = nh_set_str_create();
nh_set_str_add(header, "header");
nh_set_str_add(header, "footer");
nh_map_str_strset_set(hbr_blacklistchildren_map, "header", header);
nh_map_str_strset_set(hb_rule_blacklistchildren_map, "header", header);
// <label>
nh_set_str_t label = nh_set_str_create();
nh_set_str_add(label, "label");
nh_map_str_strset_set(hbr_blacklistchildren_map, "label", label);
nh_map_str_strset_set(hb_rule_blacklistchildren_map, "label", label);
// <progress>
nh_set_str_t progress = nh_set_str_create();
nh_set_str_add(progress, "progress");
nh_map_str_strset_set(hbr_blacklistchildren_map, "progress", progress);
nh_map_str_strset_set(hb_rule_blacklistchildren_map, "progress", progress);
// <th>
nh_set_str_t th = nh_set_str_create();
hbr_headingtags_add_elems(th);
hbr_sectioningtags_add_elems(th);
hb_rule_headingtags_add_elems(th);
hb_rule_sectioningtags_add_elems(th);
nh_set_str_add(th, "header");
nh_set_str_add(th, "footer");
nh_map_str_strset_set(hbr_blacklistchildren_map, "th", th);
nh_map_str_strset_set(hb_rule_blacklistchildren_map, "th", th);
// <video>
nh_set_str_t video = nh_set_str_create();
hbr_mediatags_add_elems(video);
nh_map_str_strset_set(hbr_blacklistchildren_map, "video", video);
hb_rule_mediatags_add_elems(video);
nh_map_str_strset_set(hb_rule_blacklistchildren_map, "video", video);
}
int hbr_blacklistchildren_allowed(hb_char_t *parent, hb_char_t *child) {
nh_set_str_t set = nh_map_str_strset_get(hbr_blacklistchildren_map, (char *) parent, NULL);
int hb_rule_blacklistchildren_allowed(hb_proc_char_t *parent, hb_proc_char_t *child) {
nh_set_str_t set = nh_map_str_strset_get(hb_rule_blacklistchildren_map, (char *) parent, NULL);
return set == NULL || !nh_set_str_has(set, (char *) child);
}

View File

@ -1,10 +1,10 @@
static nh_map_str_strset_t hbr_blacklistparents_map;
static nh_map_str_strset_t hb_rule_blacklistparents_map;
void hbr_blacklistparents_init(void) {
hbr_blacklistparents_map = nh_map_str_strset_create();
void hb_rule_blacklistparents_init(void) {
hb_rule_blacklistparents_map = nh_map_str_strset_create();
}
int hbr_blacklistparents_allowed(hb_char_t *child, hb_char_t *parent) {
nh_set_str_t set = nh_map_str_strset_get(hbr_blacklistparents_map, (char *) child, NULL);
int hb_rule_blacklistparents_allowed(hb_proc_char_t *child, hb_proc_char_t *parent) {
nh_set_str_t set = nh_map_str_strset_get(hb_rule_blacklistparents_map, (char *) child, NULL);
return set == NULL || !nh_set_str_has(set, (char *) parent);
}

View File

@ -1,52 +1,52 @@
#include "../tag/headingtags.c"
static nh_map_str_strset_t hbr_whitelistchildren_map;
static nh_map_str_strset_t hb_rule_whitelistchildren_map;
void hbr_whitelistchildren_init(void) {
hbr_whitelistchildren_map = nh_map_str_strset_create();
void hb_rule_whitelistchildren_init(void) {
hb_rule_whitelistchildren_map = nh_map_str_strset_create();
// <colgroup>
nh_set_str_t colgroup = nh_set_str_create();
nh_set_str_add(colgroup, "col");
nh_map_str_strset_set(hbr_whitelistchildren_map, "colgroup", colgroup);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "colgroup", colgroup);
// <datalist>
nh_set_str_t datalist = nh_set_str_create();
nh_set_str_add(datalist, "option");
nh_map_str_strset_set(hbr_whitelistchildren_map, "datalist", datalist);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "datalist", datalist);
// <dl>
nh_set_str_t dl = nh_set_str_create();
nh_set_str_add(dl, "dt");
nh_set_str_add(dl, "dd");
nh_map_str_strset_set(hbr_whitelistchildren_map, "dl", dl);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "dl", dl);
// <hgroup>
nh_set_str_t hgroup = nh_set_str_create();
hbr_headingtags_add_elems(hgroup);
nh_map_str_strset_set(hbr_whitelistchildren_map, "hgroup", hgroup);
hb_rule_headingtags_add_elems(hgroup);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "hgroup", hgroup);
// <ol>
nh_set_str_t ol = nh_set_str_create();
nh_set_str_add(ol, "li");
nh_map_str_strset_set(hbr_whitelistchildren_map, "ol", ol);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "ol", ol);
// <optgroup>
nh_set_str_t optgroup = nh_set_str_create();
nh_set_str_add(optgroup, "option");
nh_map_str_strset_set(hbr_whitelistchildren_map, "optgroup", optgroup);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "optgroup", optgroup);
// <picture>
nh_set_str_t picture = nh_set_str_create();
nh_set_str_add(picture, "source");
nh_set_str_add(picture, "img");
nh_map_str_strset_set(hbr_whitelistchildren_map, "picture", picture);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "picture", picture);
// <select>
nh_set_str_t select = nh_set_str_create();
nh_set_str_add(select, "optgroup");
nh_set_str_add(select, "option");
nh_map_str_strset_set(hbr_whitelistchildren_map, "select", select);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "select", select);
// <table>
nh_set_str_t table = nh_set_str_create();
@ -57,22 +57,22 @@ void hbr_whitelistchildren_init(void) {
nh_set_str_add(table, "tbody");
nh_set_str_add(table, "tfoot");
nh_set_str_add(table, "tr");
nh_map_str_strset_set(hbr_whitelistchildren_map, "table", table);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "table", table);
// <tbody>
nh_set_str_t tbody = nh_set_str_create();
nh_set_str_add(tbody, "tr");
nh_map_str_strset_set(hbr_whitelistchildren_map, "tbody", tbody);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "tbody", tbody);
// <tfoot>
nh_set_str_t tfoot = nh_set_str_create();
nh_set_str_add(tfoot, "tr");
nh_map_str_strset_set(hbr_whitelistchildren_map, "tfoot", tfoot);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "tfoot", tfoot);
// <thead>
nh_set_str_t thead = nh_set_str_create();
nh_set_str_add(thead, "tr");
nh_map_str_strset_set(hbr_whitelistchildren_map, "thead", thead);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "thead", thead);
// <tr>
nh_set_str_t tr = nh_set_str_create();
@ -80,15 +80,15 @@ void hbr_whitelistchildren_init(void) {
nh_set_str_add(tr, "th");
nh_set_str_add(tr, "template");
nh_set_str_add(tr, "script");
nh_map_str_strset_set(hbr_whitelistchildren_map, "tr", tr);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "tr", tr);
// <ul>
nh_set_str_t ul = nh_set_str_create();
nh_set_str_add(ul, "li");
nh_map_str_strset_set(hbr_whitelistchildren_map, "ul", ul);
nh_map_str_strset_set(hb_rule_whitelistchildren_map, "ul", ul);
}
int hbr_whitelistchildren_allowed(hb_char_t *parent, hb_char_t *child) {
nh_set_str_t set = nh_map_str_strset_get(hbr_whitelistchildren_map, (char *) parent, NULL);
int hb_rule_whitelistchildren_allowed(hb_proc_char_t *parent, hb_proc_char_t *child) {
nh_set_str_t set = nh_map_str_strset_get(hb_rule_whitelistchildren_map, (char *) parent, NULL);
return set == NULL || nh_set_str_has(set, (char *) child);
}

View File

@ -1,125 +1,125 @@
#include "../tag/mediatags.c"
static nh_map_str_strset_t hbr_whitelistparents_map;
static nh_map_str_strset_t hb_rule_whitelistparents_map;
void hbr_whitelistparents_init(void) {
hbr_whitelistparents_map = nh_map_str_strset_create();
void hb_rule_whitelistparents_init(void) {
hb_rule_whitelistparents_map = nh_map_str_strset_create();
// <caption>
nh_set_str_t caption = nh_set_str_create();
nh_set_str_add(caption, "table");
nh_map_str_strset_set(hbr_whitelistparents_map, "caption", caption);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "caption", caption);
// <col>
nh_set_str_t col = nh_set_str_create();
nh_set_str_add(col, "table");
nh_set_str_add(col, "colgroup");
nh_map_str_strset_set(hbr_whitelistparents_map, "col", col);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "col", col);
// <colgroup>
nh_set_str_t colgroup = nh_set_str_create();
nh_set_str_add(colgroup, "table");
nh_map_str_strset_set(hbr_whitelistparents_map, "colgroup", colgroup);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "colgroup", colgroup);
// <dd>
nh_set_str_t dd = nh_set_str_create();
nh_set_str_add(dd, "dl");
nh_map_str_strset_set(hbr_whitelistparents_map, "dd", dd);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "dd", dd);
// <dt>
nh_set_str_t dt = nh_set_str_create();
nh_set_str_add(dt, "dl");
nh_map_str_strset_set(hbr_whitelistparents_map, "dt", dt);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "dt", dt);
// <figcaption>
nh_set_str_t figcaption = nh_set_str_create();
nh_set_str_add(figcaption, "figure");
nh_map_str_strset_set(hbr_whitelistparents_map, "figcaption", figcaption);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "figcaption", figcaption);
// <legend>
nh_set_str_t legend = nh_set_str_create();
nh_set_str_add(legend, "fieldset");
nh_map_str_strset_set(hbr_whitelistparents_map, "legend", legend);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "legend", legend);
// <li>
nh_set_str_t li = nh_set_str_create();
nh_set_str_add(li, "ul");
nh_set_str_add(li, "ol");
nh_set_str_add(li, "menu");
nh_map_str_strset_set(hbr_whitelistparents_map, "li", li);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "li", li);
// <optgroup>
nh_set_str_t optgroup = nh_set_str_create();
nh_set_str_add(optgroup, "select");
nh_map_str_strset_set(hbr_whitelistparents_map, "optgroup", optgroup);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "optgroup", optgroup);
// <option>
nh_set_str_t option = nh_set_str_create();
nh_set_str_add(option, "select");
nh_set_str_add(option, "optgroup");
nh_set_str_add(option, "datalist");
nh_map_str_strset_set(hbr_whitelistparents_map, "option", option);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "option", option);
// <param>
nh_set_str_t param = nh_set_str_create();
nh_set_str_add(param, "object");
nh_map_str_strset_set(hbr_whitelistparents_map, "param", param);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "param", param);
// <rp>
nh_set_str_t rp = nh_set_str_create();
nh_set_str_add(rp, "ruby");
nh_map_str_strset_set(hbr_whitelistparents_map, "rp", rp);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "rp", rp);
// <rt>
nh_set_str_t rt = nh_set_str_create();
nh_set_str_add(rt, "ruby");
nh_map_str_strset_set(hbr_whitelistparents_map, "rt", rt);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "rt", rt);
// <rtc>
nh_set_str_t rtc = nh_set_str_create();
nh_set_str_add(rtc, "ruby");
nh_map_str_strset_set(hbr_whitelistparents_map, "rtc", rtc);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "rtc", rtc);
// <summary>
nh_set_str_t summary = nh_set_str_create();
nh_set_str_add(summary, "details");
nh_map_str_strset_set(hbr_whitelistparents_map, "summary", summary);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "summary", summary);
// <source>
nh_set_str_t source = nh_set_str_create();
hbr_mediatags_add_elems(source);
hb_rule_mediatags_add_elems(source);
nh_set_str_add(source, "picture");
nh_map_str_strset_set(hbr_whitelistparents_map, "source", source);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "source", source);
// <track>
nh_set_str_t track = nh_set_str_create();
hbr_mediatags_add_elems(track);
nh_map_str_strset_set(hbr_whitelistparents_map, "track", track);
hb_rule_mediatags_add_elems(track);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "track", track);
// <tbody>
nh_set_str_t tbody = nh_set_str_create();
nh_set_str_add(tbody, "table");
nh_map_str_strset_set(hbr_whitelistparents_map, "tbody", tbody);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "tbody", tbody);
// <td>
nh_set_str_t td = nh_set_str_create();
nh_set_str_add(td, "tr");
nh_map_str_strset_set(hbr_whitelistparents_map, "td", td);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "td", td);
// <tfoot>
nh_set_str_t tfoot = nh_set_str_create();
nh_set_str_add(tfoot, "table");
nh_map_str_strset_set(hbr_whitelistparents_map, "tfoot", tfoot);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "tfoot", tfoot);
// <th>
nh_set_str_t th = nh_set_str_create();
nh_set_str_add(th, "tr");
nh_map_str_strset_set(hbr_whitelistparents_map, "th", th);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "th", th);
// <thead>
nh_set_str_t thead = nh_set_str_create();
nh_set_str_add(thead, "table");
nh_map_str_strset_set(hbr_whitelistparents_map, "thead", thead);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "thead", thead);
// <tr>
nh_set_str_t tr = nh_set_str_create();
@ -127,13 +127,13 @@ void hbr_whitelistparents_init(void) {
nh_set_str_add(tr, "thead");
nh_set_str_add(tr, "tbody");
nh_set_str_add(tr, "tfoot");
nh_map_str_strset_set(hbr_whitelistparents_map, "tr", tr);
nh_map_str_strset_set(hb_rule_whitelistparents_map, "tr", tr);
// <template>
// Should be <body>, <frameset>, <head>, <dl>, <colgroup>, but ignoring
}
int hbr_whitelistparents_allowed(hb_char_t *child, hb_char_t *parent) {
nh_set_str_t set = nh_map_str_strset_get(hbr_whitelistparents_map, (char *) child, NULL);
int hb_rule_whitelistparents_allowed(hb_proc_char_t *child, hb_proc_char_t *parent) {
nh_set_str_t set = nh_map_str_strset_get(hb_rule_whitelistparents_map, (char *) child, NULL);
return set == NULL || nh_set_str_has(set, (char *) parent);
}

View File

@ -1,6 +1,6 @@
static nh_set_str_t hbr_contentfirsttags_set;
static nh_set_str_t hb_rule_contentfirsttags_set;
void hbr_contentfirsttags_add_elems(nh_set_str_t set) {
void hb_rule_contentfirsttags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "dd");
nh_set_str_add(set, "details");
nh_set_str_add(set, "dt");
@ -16,11 +16,11 @@ void hbr_contentfirsttags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "th");
}
void hbr_contentfirsttags_init(void) {
hbr_contentfirsttags_set = nh_set_str_create();
hbr_contentfirsttags_add_elems(hbr_contentfirsttags_set);
void hb_rule_contentfirsttags_init(void) {
hb_rule_contentfirsttags_set = nh_set_str_create();
hb_rule_contentfirsttags_add_elems(hb_rule_contentfirsttags_set);
}
int hbr_contentfirsttags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_contentfirsttags_set, (char *) tag);
int hb_rule_contentfirsttags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_contentfirsttags_set, (char *) tag);
}

View File

@ -1,6 +1,6 @@
static nh_set_str_t hbr_contenttags_set;
static nh_set_str_t hb_rule_contenttags_set;
void hbr_contenttags_add_elems(nh_set_str_t set) {
void hb_rule_contenttags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "address");
nh_set_str_add(set, "audio");
nh_set_str_add(set, "button");
@ -23,11 +23,11 @@ void hbr_contenttags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "video");
}
void hbr_contenttags_init(void) {
hbr_contenttags_set = nh_set_str_create();
hbr_contenttags_add_elems(hbr_contenttags_set);
void hb_rule_contenttags_init(void) {
hb_rule_contenttags_set = nh_set_str_create();
hb_rule_contenttags_add_elems(hb_rule_contenttags_set);
}
int hbr_contenttags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_contenttags_set, (char *) tag);
int hb_rule_contenttags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_contenttags_set, (char *) tag);
}

View File

@ -1,6 +1,6 @@
static nh_set_str_t hbr_formattingtags_set;
static nh_set_str_t hb_rule_formattingtags_set;
void hbr_formattingtags_add_elems(nh_set_str_t set) {
void hb_rule_formattingtags_add_elems(nh_set_str_t set) {
// Difference to MDN's inline text semantics list: -br, +del, +ins
nh_set_str_add(set, "a");
nh_set_str_add(set, "abbr");
@ -34,11 +34,11 @@ void hbr_formattingtags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "wbr");
}
void hbr_formattingtags_init(void) {
hbr_formattingtags_set = nh_set_str_create();
hbr_formattingtags_add_elems(hbr_formattingtags_set);
void hb_rule_formattingtags_init(void) {
hb_rule_formattingtags_set = nh_set_str_create();
hb_rule_formattingtags_add_elems(hb_rule_formattingtags_set);
}
int hbr_formattingtags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_formattingtags_set, (char *) tag);
int hb_rule_formattingtags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_formattingtags_set, (char *) tag);
}

View File

@ -1,6 +1,6 @@
static nh_set_str_t hbr_headingtags_set;
static nh_set_str_t hb_rule_headingtags_set;
void hbr_headingtags_add_elems(nh_set_str_t set) {
void hb_rule_headingtags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "hgroup");
nh_set_str_add(set, "h1");
nh_set_str_add(set, "h2");
@ -10,11 +10,11 @@ void hbr_headingtags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "h6");
}
void hbr_headingtags_init(void) {
hbr_headingtags_set = nh_set_str_create();
hbr_headingtags_add_elems(hbr_headingtags_set);
void hb_rule_headingtags_init(void) {
hb_rule_headingtags_set = nh_set_str_create();
hb_rule_headingtags_add_elems(hb_rule_headingtags_set);
}
int hbr_headingtags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_headingtags_set, (char *) tag);
int hb_rule_headingtags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_headingtags_set, (char *) tag);
}

View File

@ -1,162 +1,162 @@
// Sourced from https://developer.mozilla.org/en-US/docs/Web/HTML/Element at 2018-07-01T05:55:00Z
static nh_set_str_t hbr_htmltags_set;
static nh_set_str_t hb_rule_htmltags_set;
void hbr_htmltags_init(void) {
hbr_htmltags_set = nh_set_str_create();
nh_set_str_add(hbr_htmltags_set, "a");
nh_set_str_add(hbr_htmltags_set, "abbr");
nh_set_str_add(hbr_htmltags_set, "acronym");
nh_set_str_add(hbr_htmltags_set, "address");
nh_set_str_add(hbr_htmltags_set, "applet");
nh_set_str_add(hbr_htmltags_set, "applet");
nh_set_str_add(hbr_htmltags_set, "area");
nh_set_str_add(hbr_htmltags_set, "article");
nh_set_str_add(hbr_htmltags_set, "aside");
nh_set_str_add(hbr_htmltags_set, "audio");
nh_set_str_add(hbr_htmltags_set, "b");
nh_set_str_add(hbr_htmltags_set, "basefont");
nh_set_str_add(hbr_htmltags_set, "bdi");
nh_set_str_add(hbr_htmltags_set, "bdo");
nh_set_str_add(hbr_htmltags_set, "bgsound");
nh_set_str_add(hbr_htmltags_set, "big");
nh_set_str_add(hbr_htmltags_set, "blink");
nh_set_str_add(hbr_htmltags_set, "blockquote");
nh_set_str_add(hbr_htmltags_set, "body");
nh_set_str_add(hbr_htmltags_set, "br");
nh_set_str_add(hbr_htmltags_set, "button");
nh_set_str_add(hbr_htmltags_set, "canvas");
nh_set_str_add(hbr_htmltags_set, "caption");
nh_set_str_add(hbr_htmltags_set, "center");
nh_set_str_add(hbr_htmltags_set, "cite");
nh_set_str_add(hbr_htmltags_set, "code");
nh_set_str_add(hbr_htmltags_set, "col");
nh_set_str_add(hbr_htmltags_set, "colgroup");
nh_set_str_add(hbr_htmltags_set, "command");
nh_set_str_add(hbr_htmltags_set, "content");
nh_set_str_add(hbr_htmltags_set, "content");
nh_set_str_add(hbr_htmltags_set, "data");
nh_set_str_add(hbr_htmltags_set, "datalist");
nh_set_str_add(hbr_htmltags_set, "dd");
nh_set_str_add(hbr_htmltags_set, "del");
nh_set_str_add(hbr_htmltags_set, "details");
nh_set_str_add(hbr_htmltags_set, "dfn");
nh_set_str_add(hbr_htmltags_set, "dialog");
nh_set_str_add(hbr_htmltags_set, "dir");
nh_set_str_add(hbr_htmltags_set, "dir");
nh_set_str_add(hbr_htmltags_set, "div");
nh_set_str_add(hbr_htmltags_set, "dl");
nh_set_str_add(hbr_htmltags_set, "dt");
nh_set_str_add(hbr_htmltags_set, "element");
nh_set_str_add(hbr_htmltags_set, "element");
nh_set_str_add(hbr_htmltags_set, "em");
nh_set_str_add(hbr_htmltags_set, "embed");
nh_set_str_add(hbr_htmltags_set, "fieldset");
nh_set_str_add(hbr_htmltags_set, "figcaption");
nh_set_str_add(hbr_htmltags_set, "figure");
nh_set_str_add(hbr_htmltags_set, "font");
nh_set_str_add(hbr_htmltags_set, "footer");
nh_set_str_add(hbr_htmltags_set, "form");
nh_set_str_add(hbr_htmltags_set, "frame");
nh_set_str_add(hbr_htmltags_set, "frameset");
nh_set_str_add(hbr_htmltags_set, "h1");
nh_set_str_add(hbr_htmltags_set, "h2");
nh_set_str_add(hbr_htmltags_set, "h3");
nh_set_str_add(hbr_htmltags_set, "h4");
nh_set_str_add(hbr_htmltags_set, "h5");
nh_set_str_add(hbr_htmltags_set, "h6");
nh_set_str_add(hbr_htmltags_set, "head");
nh_set_str_add(hbr_htmltags_set, "header");
nh_set_str_add(hbr_htmltags_set, "hgroup");
nh_set_str_add(hbr_htmltags_set, "hr");
nh_set_str_add(hbr_htmltags_set, "html");
nh_set_str_add(hbr_htmltags_set, "i");
nh_set_str_add(hbr_htmltags_set, "iframe");
nh_set_str_add(hbr_htmltags_set, "image");
nh_set_str_add(hbr_htmltags_set, "img");
nh_set_str_add(hbr_htmltags_set, "input");
nh_set_str_add(hbr_htmltags_set, "ins");
nh_set_str_add(hbr_htmltags_set, "isindex");
nh_set_str_add(hbr_htmltags_set, "kbd");
nh_set_str_add(hbr_htmltags_set, "keygen");
nh_set_str_add(hbr_htmltags_set, "label");
nh_set_str_add(hbr_htmltags_set, "legend");
nh_set_str_add(hbr_htmltags_set, "li");
nh_set_str_add(hbr_htmltags_set, "link");
nh_set_str_add(hbr_htmltags_set, "listing");
nh_set_str_add(hbr_htmltags_set, "main");
nh_set_str_add(hbr_htmltags_set, "map");
nh_set_str_add(hbr_htmltags_set, "mark");
nh_set_str_add(hbr_htmltags_set, "marquee");
nh_set_str_add(hbr_htmltags_set, "menu");
nh_set_str_add(hbr_htmltags_set, "menuitem");
nh_set_str_add(hbr_htmltags_set, "menuitem");
nh_set_str_add(hbr_htmltags_set, "meta");
nh_set_str_add(hbr_htmltags_set, "meter");
nh_set_str_add(hbr_htmltags_set, "multicol");
nh_set_str_add(hbr_htmltags_set, "nav");
nh_set_str_add(hbr_htmltags_set, "nextid");
nh_set_str_add(hbr_htmltags_set, "nobr");
nh_set_str_add(hbr_htmltags_set, "noembed");
nh_set_str_add(hbr_htmltags_set, "noembed");
nh_set_str_add(hbr_htmltags_set, "noframes");
nh_set_str_add(hbr_htmltags_set, "noscript");
nh_set_str_add(hbr_htmltags_set, "object");
nh_set_str_add(hbr_htmltags_set, "ol");
nh_set_str_add(hbr_htmltags_set, "optgroup");
nh_set_str_add(hbr_htmltags_set, "option");
nh_set_str_add(hbr_htmltags_set, "output");
nh_set_str_add(hbr_htmltags_set, "p");
nh_set_str_add(hbr_htmltags_set, "param");
nh_set_str_add(hbr_htmltags_set, "picture");
nh_set_str_add(hbr_htmltags_set, "plaintext");
nh_set_str_add(hbr_htmltags_set, "pre");
nh_set_str_add(hbr_htmltags_set, "progress");
nh_set_str_add(hbr_htmltags_set, "q");
nh_set_str_add(hbr_htmltags_set, "rp");
nh_set_str_add(hbr_htmltags_set, "rt");
nh_set_str_add(hbr_htmltags_set, "rtc");
nh_set_str_add(hbr_htmltags_set, "ruby");
nh_set_str_add(hbr_htmltags_set, "s");
nh_set_str_add(hbr_htmltags_set, "samp");
nh_set_str_add(hbr_htmltags_set, "script");
nh_set_str_add(hbr_htmltags_set, "section");
nh_set_str_add(hbr_htmltags_set, "select");
nh_set_str_add(hbr_htmltags_set, "shadow");
nh_set_str_add(hbr_htmltags_set, "shadow");
nh_set_str_add(hbr_htmltags_set, "slot");
nh_set_str_add(hbr_htmltags_set, "small");
nh_set_str_add(hbr_htmltags_set, "source");
nh_set_str_add(hbr_htmltags_set, "spacer");
nh_set_str_add(hbr_htmltags_set, "span");
nh_set_str_add(hbr_htmltags_set, "strike");
nh_set_str_add(hbr_htmltags_set, "strong");
nh_set_str_add(hbr_htmltags_set, "style");
nh_set_str_add(hbr_htmltags_set, "sub");
nh_set_str_add(hbr_htmltags_set, "summary");
nh_set_str_add(hbr_htmltags_set, "sup");
nh_set_str_add(hbr_htmltags_set, "table");
nh_set_str_add(hbr_htmltags_set, "tbody");
nh_set_str_add(hbr_htmltags_set, "td");
nh_set_str_add(hbr_htmltags_set, "template");
nh_set_str_add(hbr_htmltags_set, "textarea");
nh_set_str_add(hbr_htmltags_set, "tfoot");
nh_set_str_add(hbr_htmltags_set, "th");
nh_set_str_add(hbr_htmltags_set, "thead");
nh_set_str_add(hbr_htmltags_set, "time");
nh_set_str_add(hbr_htmltags_set, "title");
nh_set_str_add(hbr_htmltags_set, "tr");
nh_set_str_add(hbr_htmltags_set, "track");
nh_set_str_add(hbr_htmltags_set, "tt");
nh_set_str_add(hbr_htmltags_set, "tt");
nh_set_str_add(hbr_htmltags_set, "u");
nh_set_str_add(hbr_htmltags_set, "ul");
nh_set_str_add(hbr_htmltags_set, "var");
nh_set_str_add(hbr_htmltags_set, "video");
nh_set_str_add(hbr_htmltags_set, "wbr");
nh_set_str_add(hbr_htmltags_set, "xmp");
void hb_rule_htmltags_init(void) {
hb_rule_htmltags_set = nh_set_str_create();
nh_set_str_add(hb_rule_htmltags_set, "a");
nh_set_str_add(hb_rule_htmltags_set, "abbr");
nh_set_str_add(hb_rule_htmltags_set, "acronym");
nh_set_str_add(hb_rule_htmltags_set, "address");
nh_set_str_add(hb_rule_htmltags_set, "applet");
nh_set_str_add(hb_rule_htmltags_set, "applet");
nh_set_str_add(hb_rule_htmltags_set, "area");
nh_set_str_add(hb_rule_htmltags_set, "article");
nh_set_str_add(hb_rule_htmltags_set, "aside");
nh_set_str_add(hb_rule_htmltags_set, "audio");
nh_set_str_add(hb_rule_htmltags_set, "b");
nh_set_str_add(hb_rule_htmltags_set, "basefont");
nh_set_str_add(hb_rule_htmltags_set, "bdi");
nh_set_str_add(hb_rule_htmltags_set, "bdo");
nh_set_str_add(hb_rule_htmltags_set, "bgsound");
nh_set_str_add(hb_rule_htmltags_set, "big");
nh_set_str_add(hb_rule_htmltags_set, "blink");
nh_set_str_add(hb_rule_htmltags_set, "blockquote");
nh_set_str_add(hb_rule_htmltags_set, "body");
nh_set_str_add(hb_rule_htmltags_set, "br");
nh_set_str_add(hb_rule_htmltags_set, "button");
nh_set_str_add(hb_rule_htmltags_set, "canvas");
nh_set_str_add(hb_rule_htmltags_set, "caption");
nh_set_str_add(hb_rule_htmltags_set, "center");
nh_set_str_add(hb_rule_htmltags_set, "cite");
nh_set_str_add(hb_rule_htmltags_set, "code");
nh_set_str_add(hb_rule_htmltags_set, "col");
nh_set_str_add(hb_rule_htmltags_set, "colgroup");
nh_set_str_add(hb_rule_htmltags_set, "command");
nh_set_str_add(hb_rule_htmltags_set, "content");
nh_set_str_add(hb_rule_htmltags_set, "content");
nh_set_str_add(hb_rule_htmltags_set, "data");
nh_set_str_add(hb_rule_htmltags_set, "datalist");
nh_set_str_add(hb_rule_htmltags_set, "dd");
nh_set_str_add(hb_rule_htmltags_set, "del");
nh_set_str_add(hb_rule_htmltags_set, "details");
nh_set_str_add(hb_rule_htmltags_set, "dfn");
nh_set_str_add(hb_rule_htmltags_set, "dialog");
nh_set_str_add(hb_rule_htmltags_set, "dir");
nh_set_str_add(hb_rule_htmltags_set, "dir");
nh_set_str_add(hb_rule_htmltags_set, "div");
nh_set_str_add(hb_rule_htmltags_set, "dl");
nh_set_str_add(hb_rule_htmltags_set, "dt");
nh_set_str_add(hb_rule_htmltags_set, "element");
nh_set_str_add(hb_rule_htmltags_set, "element");
nh_set_str_add(hb_rule_htmltags_set, "em");
nh_set_str_add(hb_rule_htmltags_set, "embed");
nh_set_str_add(hb_rule_htmltags_set, "fieldset");
nh_set_str_add(hb_rule_htmltags_set, "figcaption");
nh_set_str_add(hb_rule_htmltags_set, "figure");
nh_set_str_add(hb_rule_htmltags_set, "font");
nh_set_str_add(hb_rule_htmltags_set, "footer");
nh_set_str_add(hb_rule_htmltags_set, "form");
nh_set_str_add(hb_rule_htmltags_set, "frame");
nh_set_str_add(hb_rule_htmltags_set, "frameset");
nh_set_str_add(hb_rule_htmltags_set, "h1");
nh_set_str_add(hb_rule_htmltags_set, "h2");
nh_set_str_add(hb_rule_htmltags_set, "h3");
nh_set_str_add(hb_rule_htmltags_set, "h4");
nh_set_str_add(hb_rule_htmltags_set, "h5");
nh_set_str_add(hb_rule_htmltags_set, "h6");
nh_set_str_add(hb_rule_htmltags_set, "head");
nh_set_str_add(hb_rule_htmltags_set, "header");
nh_set_str_add(hb_rule_htmltags_set, "hgroup");
nh_set_str_add(hb_rule_htmltags_set, "hr");
nh_set_str_add(hb_rule_htmltags_set, "html");
nh_set_str_add(hb_rule_htmltags_set, "i");
nh_set_str_add(hb_rule_htmltags_set, "iframe");
nh_set_str_add(hb_rule_htmltags_set, "image");
nh_set_str_add(hb_rule_htmltags_set, "img");
nh_set_str_add(hb_rule_htmltags_set, "input");
nh_set_str_add(hb_rule_htmltags_set, "ins");
nh_set_str_add(hb_rule_htmltags_set, "isindex");
nh_set_str_add(hb_rule_htmltags_set, "kbd");
nh_set_str_add(hb_rule_htmltags_set, "keygen");
nh_set_str_add(hb_rule_htmltags_set, "label");
nh_set_str_add(hb_rule_htmltags_set, "legend");
nh_set_str_add(hb_rule_htmltags_set, "li");
nh_set_str_add(hb_rule_htmltags_set, "link");
nh_set_str_add(hb_rule_htmltags_set, "listing");
nh_set_str_add(hb_rule_htmltags_set, "main");
nh_set_str_add(hb_rule_htmltags_set, "map");
nh_set_str_add(hb_rule_htmltags_set, "mark");
nh_set_str_add(hb_rule_htmltags_set, "marquee");
nh_set_str_add(hb_rule_htmltags_set, "menu");
nh_set_str_add(hb_rule_htmltags_set, "menuitem");
nh_set_str_add(hb_rule_htmltags_set, "menuitem");
nh_set_str_add(hb_rule_htmltags_set, "meta");
nh_set_str_add(hb_rule_htmltags_set, "meter");
nh_set_str_add(hb_rule_htmltags_set, "multicol");
nh_set_str_add(hb_rule_htmltags_set, "nav");
nh_set_str_add(hb_rule_htmltags_set, "nextid");
nh_set_str_add(hb_rule_htmltags_set, "nobr");
nh_set_str_add(hb_rule_htmltags_set, "noembed");
nh_set_str_add(hb_rule_htmltags_set, "noembed");
nh_set_str_add(hb_rule_htmltags_set, "noframes");
nh_set_str_add(hb_rule_htmltags_set, "noscript");
nh_set_str_add(hb_rule_htmltags_set, "object");
nh_set_str_add(hb_rule_htmltags_set, "ol");
nh_set_str_add(hb_rule_htmltags_set, "optgroup");
nh_set_str_add(hb_rule_htmltags_set, "option");
nh_set_str_add(hb_rule_htmltags_set, "output");
nh_set_str_add(hb_rule_htmltags_set, "p");
nh_set_str_add(hb_rule_htmltags_set, "param");
nh_set_str_add(hb_rule_htmltags_set, "picture");
nh_set_str_add(hb_rule_htmltags_set, "plaintext");
nh_set_str_add(hb_rule_htmltags_set, "pre");
nh_set_str_add(hb_rule_htmltags_set, "progress");
nh_set_str_add(hb_rule_htmltags_set, "q");
nh_set_str_add(hb_rule_htmltags_set, "rp");
nh_set_str_add(hb_rule_htmltags_set, "rt");
nh_set_str_add(hb_rule_htmltags_set, "rtc");
nh_set_str_add(hb_rule_htmltags_set, "ruby");
nh_set_str_add(hb_rule_htmltags_set, "s");
nh_set_str_add(hb_rule_htmltags_set, "samp");
nh_set_str_add(hb_rule_htmltags_set, "script");
nh_set_str_add(hb_rule_htmltags_set, "section");
nh_set_str_add(hb_rule_htmltags_set, "select");
nh_set_str_add(hb_rule_htmltags_set, "shadow");
nh_set_str_add(hb_rule_htmltags_set, "shadow");
nh_set_str_add(hb_rule_htmltags_set, "slot");
nh_set_str_add(hb_rule_htmltags_set, "small");
nh_set_str_add(hb_rule_htmltags_set, "source");
nh_set_str_add(hb_rule_htmltags_set, "spacer");
nh_set_str_add(hb_rule_htmltags_set, "span");
nh_set_str_add(hb_rule_htmltags_set, "strike");
nh_set_str_add(hb_rule_htmltags_set, "strong");
nh_set_str_add(hb_rule_htmltags_set, "style");
nh_set_str_add(hb_rule_htmltags_set, "sub");
nh_set_str_add(hb_rule_htmltags_set, "summary");
nh_set_str_add(hb_rule_htmltags_set, "sup");
nh_set_str_add(hb_rule_htmltags_set, "table");
nh_set_str_add(hb_rule_htmltags_set, "tbody");
nh_set_str_add(hb_rule_htmltags_set, "td");
nh_set_str_add(hb_rule_htmltags_set, "template");
nh_set_str_add(hb_rule_htmltags_set, "textarea");
nh_set_str_add(hb_rule_htmltags_set, "tfoot");
nh_set_str_add(hb_rule_htmltags_set, "th");
nh_set_str_add(hb_rule_htmltags_set, "thead");
nh_set_str_add(hb_rule_htmltags_set, "time");
nh_set_str_add(hb_rule_htmltags_set, "title");
nh_set_str_add(hb_rule_htmltags_set, "tr");
nh_set_str_add(hb_rule_htmltags_set, "track");
nh_set_str_add(hb_rule_htmltags_set, "tt");
nh_set_str_add(hb_rule_htmltags_set, "tt");
nh_set_str_add(hb_rule_htmltags_set, "u");
nh_set_str_add(hb_rule_htmltags_set, "ul");
nh_set_str_add(hb_rule_htmltags_set, "var");
nh_set_str_add(hb_rule_htmltags_set, "video");
nh_set_str_add(hb_rule_htmltags_set, "wbr");
nh_set_str_add(hb_rule_htmltags_set, "xmp");
}
int hbr_htmltags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_htmltags_set, (char *) tag);
int hb_rule_htmltags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_htmltags_set, (char *) tag);
}

View File

@ -1,9 +1,9 @@
#include "./sectioningtags.c"
static nh_set_str_t hbr_layouttags_set;
static nh_set_str_t hb_rule_layouttags_set;
void hbr_layouttags_add_elems(nh_set_str_t set) {
hbr_sectioningtags_add_elems(set);
void hb_rule_layouttags_add_elems(nh_set_str_t set) {
hb_rule_sectioningtags_add_elems(set);
nh_set_str_add(set, "blockquote");
nh_set_str_add(set, "body");
nh_set_str_add(set, "colgroup");
@ -36,11 +36,11 @@ void hbr_layouttags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "ul");
}
void hbr_layouttags_init(void) {
hbr_layouttags_set = nh_set_str_create();
hbr_layouttags_add_elems(hbr_layouttags_set);
void hb_rule_layouttags_init(void) {
hb_rule_layouttags_set = nh_set_str_create();
hb_rule_layouttags_add_elems(hb_rule_layouttags_set);
}
int hbr_layouttags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_layouttags_set, (char *) tag);
int hb_rule_layouttags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_layouttags_set, (char *) tag);
}

View File

@ -1,15 +1,15 @@
static nh_set_str_t hbr_mediatags_set;
static nh_set_str_t hb_rule_mediatags_set;
void hbr_mediatags_add_elems(nh_set_str_t set) {
void hb_rule_mediatags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "audio");
nh_set_str_add(set, "video");
}
void hbr_mediatags_init(void) {
hbr_mediatags_set = nh_set_str_create();
hbr_mediatags_add_elems(hbr_mediatags_set);
void hb_rule_mediatags_init(void) {
hb_rule_mediatags_set = nh_set_str_create();
hb_rule_mediatags_add_elems(hb_rule_mediatags_set);
}
int hbr_mediatags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_mediatags_set, (char *) tag);
int hb_rule_mediatags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_mediatags_set, (char *) tag);
}

View File

@ -1,17 +1,17 @@
static nh_set_str_t hbr_sectioningtags_set;
static nh_set_str_t hb_rule_sectioningtags_set;
void hbr_sectioningtags_add_elems(nh_set_str_t set) {
void hb_rule_sectioningtags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "article");
nh_set_str_add(set, "aside");
nh_set_str_add(set, "nav");
nh_set_str_add(set, "section");
}
void hbr_sectioningtags_init(void) {
hbr_sectioningtags_set = nh_set_str_create();
hbr_sectioningtags_add_elems(hbr_sectioningtags_set);
void hb_rule_sectioningtags_init(void) {
hb_rule_sectioningtags_set = nh_set_str_create();
hb_rule_sectioningtags_add_elems(hb_rule_sectioningtags_set);
}
int hbr_sectioningtags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_sectioningtags_set, (char *) tag);
int hb_rule_sectioningtags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_sectioningtags_set, (char *) tag);
}

View File

@ -1,10 +1,10 @@
#include "./sectioningtags.c"
#include "./svgtags.c"
static nh_set_str_t hbr_specifictags_set;
static nh_set_str_t hb_rule_specifictags_set;
void hbr_specifictags_add_elems(nh_set_str_t set) {
hbr_svgtags_add_elems(set);
void hb_rule_specifictags_add_elems(nh_set_str_t set) {
hb_rule_svgtags_add_elems(set);
nh_set_str_add(set, "area");
nh_set_str_add(set, "base");
nh_set_str_add(set, "br");
@ -21,11 +21,11 @@ void hbr_specifictags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "track");
}
void hbr_specifictags_init(void) {
hbr_specifictags_set = nh_set_str_create();
hbr_specifictags_add_elems(hbr_specifictags_set);
void hb_rule_specifictags_init(void) {
hb_rule_specifictags_set = nh_set_str_create();
hb_rule_specifictags_add_elems(hb_rule_specifictags_set);
}
int hbr_specifictags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_specifictags_set, (char *) tag);
int hb_rule_specifictags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_specifictags_set, (char *) tag);
}

View File

@ -1,8 +1,8 @@
// Sourced from https://developer.mozilla.org/en-US/docs/Web/SVG/Element at 2018-08-04T03:50:00Z
static nh_set_str_t hbr_svgtags_set;
static nh_set_str_t hb_rule_svgtags_set;
void hbr_svgtags_add_elems(nh_set_str_t set) {
void hb_rule_svgtags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "a");
nh_set_str_add(set, "altGlyph");
nh_set_str_add(set, "altGlyphDef");
@ -95,11 +95,11 @@ void hbr_svgtags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "vkern");
}
void hbr_svgtags_init(void) {
hbr_svgtags_set = nh_set_str_create();
hbr_svgtags_add_elems(hbr_svgtags_set);
void hb_rule_svgtags_init(void) {
hb_rule_svgtags_set = nh_set_str_create();
hb_rule_svgtags_add_elems(hb_rule_svgtags_set);
}
int hbr_svgtags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_svgtags_set, (char *) tag);
int hb_rule_svgtags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_svgtags_set, (char *) tag);
}

View File

@ -1,10 +1,10 @@
#include "./htmltags.c"
#include "./svgtags.c"
void hbr_tags_init(void) {
void hb_rule_tags_init(void) {
}
int hbr_tags_check(hb_char_t *tag) {
return hbr_htmltags_check(tag) ||
hbr_svgtags_check(tag);
int hb_rule_tags_check(hb_proc_char_t *tag) {
return hb_rule_htmltags_check(tag) ||
hb_rule_svgtags_check(tag);
}

View File

@ -1,6 +1,6 @@
static nh_set_str_t hbr_voidtags_set;
static nh_set_str_t hb_rule_voidtags_set;
void hbr_voidtags_add_elems(nh_set_str_t set) {
void hb_rule_voidtags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "area");
nh_set_str_add(set, "base");
nh_set_str_add(set, "br");
@ -18,11 +18,11 @@ void hbr_voidtags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "wbr");
}
void hbr_voidtags_init(void) {
hbr_voidtags_set = nh_set_str_create();
hbr_voidtags_add_elems(hbr_voidtags_set);
void hb_rule_voidtags_init(void) {
hb_rule_voidtags_set = nh_set_str_create();
hb_rule_voidtags_add_elems(hb_rule_voidtags_set);
}
int hbr_voidtags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_voidtags_set, (char *) tag);
int hb_rule_voidtags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_voidtags_set, (char *) tag);
}

View File

@ -1,15 +1,15 @@
static nh_set_str_t hbr_wsstags_set;
static nh_set_str_t hb_rule_wsstags_set;
void hbr_wsstags_add_elems(nh_set_str_t set) {
void hb_rule_wsstags_add_elems(nh_set_str_t set) {
nh_set_str_add(set, "code");
nh_set_str_add(set, "pre");
}
void hbr_wsstags_init(void) {
hbr_wsstags_set = nh_set_str_create();
hbr_wsstags_add_elems(hbr_wsstags_set);
void hb_rule_wsstags_init(void) {
hb_rule_wsstags_set = nh_set_str_create();
hb_rule_wsstags_add_elems(hb_rule_wsstags_set);
}
int hbr_wsstags_check(hb_char_t *tag) {
return nh_set_str_has(hbr_wsstags_set, (char *) tag);
int hb_rule_wsstags_check(hb_proc_char_t *tag) {
return nh_set_str_has(hb_rule_wsstags_set, (char *) tag);
}

View File

@ -1,14 +0,0 @@
void hbs_bang(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
HBE_CATCH_V(hbu_pipe_require_match, pipe, "<!");
while (1) {
hb_char_t next = HBE_CATCH_V(hbu_pipe_peek, pipe);
if (next == '>') {
break;
}
HBE_CATCH_V(hbu_pipe_accept, pipe);
}
HBE_CATCH_V(hbu_pipe_accept, pipe);
}

View File

@ -1,16 +0,0 @@
void hbs_comment(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe) {
hbu_pipe_toggle_output_mask(pipe, so->remove_comments);
HBE_CATCH_V(hbu_pipe_require_match, pipe, "<!--");
while (1) {
int end = HBE_CATCH_V(hbu_pipe_accept_if_matches, pipe, "-->");
if (end) {
break;
}
HBE_CATCH_V(hbu_pipe_accept, pipe);
}
hbu_pipe_toggle_output_mask(pipe, 0);
}

View File

@ -1,126 +0,0 @@
static void _hbs_script_slcomment(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
HBE_CATCH_V(hbu_pipe_require_match, pipe, "//");
// Comment can end at closing </script>
// NOTE: Closing tag must not contain whitespace
while (1) {
int line_term = HBE_CATCH_V(hbu_pipe_accept_if_matches_line_terminator, pipe);
if (line_term) {
break;
}
int end_tag = HBE_CATCH_V(hbu_pipe_matches_i, pipe, "</script>");
if (end_tag) {
break;
}
HBE_CATCH_V(hbu_pipe_accept, pipe);
}
}
static void _hbs_script_mlcomment(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
HBE_CATCH_V(hbu_pipe_require_match, pipe, "/*");
// Comment can end at closing </script>
// NOTE: Closing tag must not contain whitespace
while (1) {
int end = HBE_CATCH_V(hbu_pipe_accept_if_matches, pipe, "*/");
if (end) {
break;
}
int end_tag = HBE_CATCH_V(hbu_pipe_matches_i, pipe, "</script>");
if (end_tag) {
break;
}
HBE_CATCH_V(hbu_pipe_accept, pipe);
}
}
static void _hbs_script_string(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
hb_char_t delim = HBE_CATCH_V(hbu_pipe_accept, pipe);
if (delim != '"' && delim != '\'') {
HBU_PIPE_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected JavaScript string delimiter");
}
int escaping = 0;
while (1) {
hb_char_t c = HBE_CATCH_V(hbu_pipe_accept, pipe);
if (c == '\\') {
escaping ^= 1;
continue;
}
if (c == delim && !escaping) {
break;
}
int line_term = HBE_CATCH_V(hbu_pipe_accept_if_matches_line_terminator, pipe);
if (line_term) {
if (!escaping) {
HBU_PIPE_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Unterminated JavaScript string");
}
}
escaping = 0;
}
}
static void _hbs_script_template(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
HBE_CATCH_V(hbu_pipe_require_match, pipe, "`");
int escaping = 0;
while (1) {
hb_char_t c = HBE_CATCH_V(hbu_pipe_accept, pipe);
if (c == '\\') {
escaping ^= 1;
continue;
}
if (c == '`' && !escaping) {
break;
}
escaping = 0;
}
}
void hbs_script(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
while (1) {
int end = HBE_CATCH_V(hbu_pipe_matches, pipe, "</");
if (end) {
break;
}
int sl_comment = HBE_CATCH_V(hbu_pipe_matches, pipe, "//");
if (sl_comment) {
HBE_CATCH_V(_hbs_script_slcomment, pipe);
continue;
}
int ml_comment = HBE_CATCH_V(hbu_pipe_matches, pipe, "/*");
if (ml_comment) {
HBE_CATCH_V(_hbs_script_mlcomment, pipe);
continue;
}
hb_char_t next = HBE_CATCH_V(hbu_pipe_peek, pipe);
if (next == '"' || next == '\'') {
HBE_CATCH_V(_hbs_script_string, pipe);
continue;
}
if (next == '`') {
HBE_CATCH_V(_hbs_script_template, pipe);
continue;
}
HBE_CATCH_V(hbu_pipe_accept, pipe);
}
}

View File

@ -1,67 +0,0 @@
static void _hbs_style_comment(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
HBE_CATCH_V(hbu_pipe_require_match, pipe, "/*");
// Unlike script tags, style comments do NOT end at closing tag
while (1) {
int is_end = HBE_CATCH_V(hbu_pipe_accept_if_matches, pipe, "*/");
if (is_end) {
break;
}
HBE_CATCH_V(hbu_pipe_accept, pipe);
}
}
static void _hbs_style_string(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
hb_char_t delim = HBE_CATCH_V(hbu_pipe_accept, pipe);
if (delim != '"' && delim != '\'') {
HBU_PIPE_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected CSS string delimiter");
}
int escaping = 0;
while (1) {
hb_char_t c = HBE_CATCH_V(hbu_pipe_accept, pipe);
if (c == '\\') {
escaping ^= 1;
continue;
}
if (c == delim && !escaping) {
break;
}
int line_term = HBE_CATCH_V(hbu_pipe_accept_if_matches_line_terminator, pipe);
if (line_term) {
if (!escaping) {
HBU_PIPE_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Unterminated CSS string");
}
}
escaping = 0;
}
}
void hbs_style(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
while (1) {
int end = HBE_CATCH_V(hbu_pipe_matches, pipe, "</");
if (end) {
break;
}
int is_comment = HBE_CATCH_V(hbu_pipe_matches, pipe, "/*");
if (is_comment) {
HBE_CATCH_V(_hbs_style_comment, pipe);
continue;
}
hb_char_t next = HBE_CATCH_V(hbu_pipe_peek, pipe);
if (next == '"' || next == '\'') {
HBE_CATCH_V(_hbs_style_string, pipe);
continue;
}
HBE_CATCH_V(hbu_pipe_accept, pipe);
}
}

View File

@ -1,102 +0,0 @@
// Declare first before content.c, as content.c depends on it
void hbs_tag(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe, hb_char_t *parent);
#include "./tagname.c"
#include "../attr/attr.c"
#include "../content/script.c"
#include "../content/style.c"
#include "../content/html.c"
// $parent could be NULL
void hbs_tag(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe, hb_char_t *parent) {
hbu_list_char_t opening_name = NULL;
int self_closing = 0;
HBE_CATCH_F(hbu_pipe_require, pipe, '<');
opening_name = HBE_CATCH_F(hbs_tagname, so, pipe);
int last_attr_type = -1;
while (1) {
// At the beginning of this loop, the last parsed unit was either the tag name
// or an attribute (including its value, if it had one)
size_t ws_accepted;
if (so->remove_tag_whitespace) {
ws_accepted = HBE_CATCH_F(hbu_pipe_skip_while_predicate, pipe, &hbr_whitespace_check);
} else {
ws_accepted = HBE_CATCH_F(hbu_pipe_accept_while_predicate, pipe, &hbr_whitespace_check);
}
int end_of_tag = HBE_CATCH_F(hbu_pipe_accept_if, pipe, '>');
if (end_of_tag) {
break;
}
self_closing = HBE_CATCH_F(hbu_pipe_accept_if_matches, pipe, "/>");
if (self_closing) {
if (!hbu_streamoptions_supressed_error(so, HBE_PARSE_SELF_CLOSING_TAG)) {
HBU_PIPE_THROW_F(pipe, HBE_PARSE_SELF_CLOSING_TAG, "Self-closing tag");
}
break;
}
// HBE_PARSE_NO_SPACE_BEFORE_ATTR is not suppressable as then there would be difficulty
// in determining what is the end of a tag/attribute name/attribute value
if (!ws_accepted) {
HBU_PIPE_THROW_F(pipe, HBE_PARSE_NO_SPACE_BEFORE_ATTR, "No whitespace before attribute");
}
if (so->remove_tag_whitespace) {
if (last_attr_type != HBS_ATTR_QUOTED) {
HBE_CATCH_F(hbu_pipe_write, pipe, ' ');
}
}
last_attr_type = HBE_CATCH_F(hbs_attr, so, pipe);
}
hb_char_t *tag_name = hbu_list_char_underlying(opening_name);
// Non-standard tag checking is done in hbs_tagname
if (parent != NULL && (
!hbr_whitelistparents_allowed(tag_name, parent) ||
!hbr_whitelistchildren_allowed(parent, tag_name) ||
!hbr_blacklistparents_allowed(tag_name, parent) ||
!hbr_blacklistchildren_allowed(parent, tag_name))) {
HBU_PIPE_THROW_F(pipe, HBE_PARSE_ILLEGAL_CHILD, "Tag can't be a child there");
}
// Self-closing or void tag
if (self_closing || hbr_voidtags_check(tag_name)) {
goto finally;
}
if (hbu_list_char_compare_lit(opening_name, "script") == 0) {
// Script tag
HBE_CATCH_F(hbs_script, pipe);
} else if (hbu_list_char_compare_lit(opening_name, "style") == 0) {
// Style tag
HBE_CATCH_F(hbs_style, pipe);
} else {
// Content
HBE_CATCH_F(hbs_content, so, pipe, tag_name);
}
// Closing tag for non-void
HBE_CATCH_F(hbu_pipe_require, pipe, '<');
HBE_CATCH_F(hbu_pipe_require, pipe, '/');
hbu_list_char_t closing_name = HBE_CATCH_F(hbs_tagname, so, pipe);
HBE_CATCH_F(hbu_pipe_require, pipe, '>');
if (!hbu_list_char_equal(opening_name, closing_name)) {
HBU_PIPE_THROW_F(pipe, HBE_PARSE_UNCLOSED_TAG, "Tag not closed (expected `%s` closing tag, got `%s`)", tag_name, hbu_list_char_underlying(closing_name));
}
finally:
if (opening_name) {
hbu_list_char_destroy(opening_name);
opening_name = NULL;
}
return;
}

View File

@ -1,34 +0,0 @@
hbu_list_char_t hbs_tagname(hbe_err_t *hbe_err, hbu_streamoptions_t so, hbu_pipe_t pipe) {
hbu_list_char_t name = hbu_list_char_create();
while (1) {
hb_char_t c = HBE_CATCH_F(hbu_pipe_peek, pipe);
if (!hbr_tagname_check(c)) {
break;
}
if (hbr_ucalpha_check(c)) {
if (!hbu_streamoptions_supressed_error(so, HBE_PARSE_UCASE_TAG)) {
HBU_PIPE_THROW_F(pipe, HBE_PARSE_UCASE_TAG, "Uppercase character in tag");
}
// Lowercase to normalise when checking against rules and closing tag
hbu_list_char_append(name, c + 32);
} else {
hbu_list_char_append(name, c);
}
HBE_CATCH_F(hbu_pipe_accept, pipe);
}
if (!hbu_streamoptions_supressed_error(so, HBE_PARSE_NONSTANDARD_TAG) && !hbr_tags_check(hbu_list_char_underlying(name))) {
HBU_PIPE_THROW_F(pipe, HBE_PARSE_NONSTANDARD_TAG, "Non-standard tag");
}
finally:
if (*hbe_err != NULL) {
hbu_list_char_destroy(name);
name = NULL;
}
return name;
}

View File

@ -1,19 +0,0 @@
#include "./char/char.c"
#include "./config/streamoptions.c"
#include "./execution/error.c"
#include "./execution/logging.c"
#include "./fstream/fstreamin.c"
#include "./fstream/fstreamout.c"
#include "./list/char.c"
#include "./list/charlist.c"
#include "./map/str-int32.c"
#include "./map/str-strset.c"
#include "./math/math.c"
#include "./pipe/pipe.c"

View File

@ -1,5 +0,0 @@
typedef signed int hb_eod_char_t;
typedef unsigned char hb_char_t;
#define HB_EOD -1 // End Of Data
#define SIZEOF_CHAR sizeof(hb_char_t)

View File

@ -1,224 +0,0 @@
#include "../execution/error.c"
#include "../execution/logging.c"
#include "../char/char.c"
#include "../list/char.c"
#include "../list/charlist.c"
#include "../../rule/tag/contentfirsttags.c"
#include "../../rule/tag/contenttags.c"
#include "../../rule/tag/formattingtags.c"
#include "../../rule/tag/headingtags.c"
#include "../../rule/tag/layouttags.c"
#include "../../rule/tag/mediatags.c"
#include "../../rule/tag/sectioningtags.c"
#include "../../rule/tag/specifictags.c"
#include "../../rule/tag/tags.c"
#include "../../rule/tag/voidtags.c"
#include "../../rule/tag/wsstags.c"
typedef struct hbu_streamoptions_s {
nh_set_str_t ex_collapse_whitespace; // Could be NULL to represent the universal set (i.e. don't minify)
nh_set_str_t ex_destroy_whole_whitespace; // Could be NULL to represent the universal set (i.e. don't minify)
nh_set_str_t ex_trim_whitespace; // Could be NULL to represent the universal set (i.e. don't minify)
nh_set_int32_t suppressed_errors;
int trim_class_attr;
int decode_entities;
int min_conditional_comments;
int remove_attr_quotes;
int remove_comments;
int remove_optional_tags;
int remove_tag_whitespace;
} *hbu_streamoptions_t;
static nh_set_str_t _hbu_streamoptions_default_ex_collapse_whitespace = NULL;
nh_set_str_t hbu_streamoptions_default_ex_collapse_whitespace(void) {
if (_hbu_streamoptions_default_ex_collapse_whitespace == NULL) {
_hbu_streamoptions_default_ex_collapse_whitespace = nh_set_str_create();
hbr_wsstags_add_elems(_hbu_streamoptions_default_ex_collapse_whitespace);
}
return _hbu_streamoptions_default_ex_collapse_whitespace;
}
static nh_set_str_t _hbu_streamoptions_default_ex_destroy_whole_whitespace = NULL;
nh_set_str_t hbu_streamoptions_default_ex_destroy_whole_whitespace(void) {
if (_hbu_streamoptions_default_ex_destroy_whole_whitespace == NULL) {
_hbu_streamoptions_default_ex_destroy_whole_whitespace = nh_set_str_create();
hbr_wsstags_add_elems(_hbu_streamoptions_default_ex_destroy_whole_whitespace);
hbr_contenttags_add_elems(_hbu_streamoptions_default_ex_destroy_whole_whitespace);
hbr_formattingtags_add_elems(_hbu_streamoptions_default_ex_destroy_whole_whitespace);
}
return _hbu_streamoptions_default_ex_destroy_whole_whitespace;
}
static nh_set_str_t _hbu_streamoptions_default_ex_trim_whitespace = NULL;
nh_set_str_t hbu_streamoptions_default_ex_trim_whitespace(void) {
if (_hbu_streamoptions_default_ex_trim_whitespace == NULL) {
_hbu_streamoptions_default_ex_trim_whitespace = nh_set_str_create();
hbr_wsstags_add_elems(_hbu_streamoptions_default_ex_trim_whitespace);
hbr_formattingtags_add_elems(_hbu_streamoptions_default_ex_trim_whitespace);
}
return _hbu_streamoptions_default_ex_trim_whitespace;
}
// WARNING: Rules must be initialised before calling this function
hbu_streamoptions_t hbu_streamoptions_create(void) {
hbu_streamoptions_t opt = malloc(sizeof(struct hbu_streamoptions_s));
opt->ex_collapse_whitespace = NULL;
opt->ex_destroy_whole_whitespace = NULL;
opt->ex_trim_whitespace = NULL;
opt->suppressed_errors = nh_set_int32_create();
opt->trim_class_attr = 1;
opt->decode_entities = 1;
opt->min_conditional_comments = 1;
opt->remove_attr_quotes = 1;
opt->remove_comments = 1;
opt->remove_optional_tags = 1;
opt->remove_tag_whitespace = 1;
return opt;
}
// WARNING: Does not destroy ex_* members
void hbu_streamoptions_destroy(hbu_streamoptions_t opt) {
nh_set_int32_destroy(opt->suppressed_errors);
free(opt);
}
int hbu_streamoptions_in_tags_list(nh_set_str_t set, hb_char_t *query) {
if (set == NULL) {
return 1; // NULL represents the universal set
}
if (query == NULL) {
// When parent is #root
return 0;
}
return nh_set_str_has(set, (char *) query);
}
int hbu_streamoptions_supressed_error(hbu_streamoptions_t opt, hbe_errcode_t errcode) {
return nh_set_int32_has(opt->suppressed_errors, errcode);
}
void hbu_streamoptions_log(hbu_streamoptions_t opt) {
hbl_info_kv_boolean("Trim `class` attributes", opt->trim_class_attr);
hbl_info_kv_boolean("Decode entities", opt->decode_entities);
hbl_info_kv_boolean("Minify conditional comments", opt->min_conditional_comments);
hbl_info_kv_boolean("Remove attribute quotes", opt->remove_attr_quotes);
hbl_info_kv_boolean("Remove comments", opt->remove_comments);
hbl_info_kv_boolean("Remove optional tags", opt->remove_optional_tags);
hbl_info_kv_boolean("Remove tag whitespace", opt->remove_tag_whitespace);
}
void hbu_streamoptions_parse_and_add_tag_set(hbe_err_t *hbe_err, char *set_name, nh_set_str_t set) {
if (strcmp(set_name, "content") == 0) {
hbr_contenttags_add_elems(set);
} else if (strcmp(set_name, "contentfirst") == 0) {
hbr_contentfirsttags_add_elems(set);
} else if (strcmp(set_name, "formatting") == 0) {
hbr_formattingtags_add_elems(set);
} else if (strcmp(set_name, "layout") == 0) {
hbr_layouttags_add_elems(set);
} else if (strcmp(set_name, "specific") == 0) {
hbr_specifictags_add_elems(set);
} else if (strcmp(set_name, "heading") == 0) {
hbr_headingtags_add_elems(set);
} else if (strcmp(set_name, "media") == 0) {
hbr_mediatags_add_elems(set);
} else if (strcmp(set_name, "sectioning") == 0) {
hbr_sectioningtags_add_elems(set);
} else if (strcmp(set_name, "void") == 0) {
hbr_voidtags_add_elems(set);
} else if (strcmp(set_name, "wss") == 0) {
hbr_wsstags_add_elems(set);
} else {
HBE_THROW_V(HBE_CLI_INVALID_TAG_SET, "Unrecognised tag set `%s`", set_name);
}
}
nh_set_str_t hbu_streamoptions_parse_list_of_tags(hbe_err_t *hbe_err, char *argv) {
nh_set_str_t set = NULL;
hbu_list_charlist_t list = NULL;
if (argv != NULL && strcmp(argv, "*")) {
return NULL;
}
set = nh_set_str_create();
if (argv == NULL) {
return set;
}
list = hbu_list_charlist_create_from_split((hb_char_t *) argv, ',');
for (size_t i = 0; i < list->length; i++) {
hbu_list_char_t part = hbu_list_charlist_get(list, i);
hb_char_t *part_c = hbu_list_char_underlying(part);
if (hbu_list_char_get(part, 0) == '$') {
// Set of tags
hbu_list_char_shift(part);
HBE_CATCH_F(hbu_streamoptions_parse_and_add_tag_set, (char *) part_c, set);
} else {
// Single tag
if (!hbr_tags_check(part_c)) {
HBE_THROW_F(HBE_CLI_INVALID_TAG, "%s is not a standard tag and was provided as part of an argument's value", part_c);
}
nh_set_str_add(set, (char *) hbu_list_char_underlying_copy(part));
}
}
finally:
if (list != NULL) {
hbu_list_charlist_destroy_from_split(list);
list = NULL;
}
if (*hbe_err != NULL) {
if (set != NULL) {
nh_set_str_destroy(set);
set = NULL;
}
}
return set;
}
void hbu_streamoptions_parse_and_add_errors_to_suppress(hbe_err_t *hbe_err, nh_set_int32_t suppressed_errors, char *argv) {
hbu_list_charlist_t list = NULL;
if (argv == NULL) {
return;
}
list = hbu_list_charlist_create_from_split((hb_char_t *) argv, ',');
for (size_t i = 0; i < list->length; i++) {
hbu_list_char_t part = hbu_list_charlist_get(list, i);
if (hbu_list_char_compare_lit(part, "MALFORMED_ENTITY") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_MALFORMED_ENTITY);
} else if (hbu_list_char_compare_lit(part, "BARE_AMPERSAND") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_BARE_AMPERSAND);
} else if (hbu_list_char_compare_lit(part, "INVALID_ENTITY") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_INVALID_ENTITY);
} else if (hbu_list_char_compare_lit(part, "NONSTANDARD_TAG") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_NONSTANDARD_TAG);
} else if (hbu_list_char_compare_lit(part, "UCASE_ATTR") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_UCASE_ATTR);
} else if (hbu_list_char_compare_lit(part, "UCASE_TAG") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_UCASE_TAG);
} else if (hbu_list_char_compare_lit(part, "UNQUOTED_ATTR") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_UNQUOTED_ATTR);
} else if (hbu_list_char_compare_lit(part, "SELF_CLOSING_TAG") == 0) {
nh_set_int32_add(suppressed_errors, HBE_PARSE_SELF_CLOSING_TAG);
} else {
HBE_THROW_F(HBE_CLI_INVALID_SUPPRESSABLE_ERROR, "Unrecognised suppressable error `%s`", hbu_list_char_underlying(part));
}
}
finally:
if (list != NULL) {
hbu_list_charlist_destroy_from_split(list);
list = NULL;
}
}

View File

@ -1,80 +0,0 @@
#include <errno.h>
#include <unistd.h>
#define HBE_COND if (*hbe_err != 0)
#define HBE_THROW(code, format, ...) *hbe_err = hbe_err_create(code, format, ##__VA_ARGS__); return 0;
#define HBE_THROW_V(code, format, ...) *hbe_err = hbe_err_create(code, format, ##__VA_ARGS__); return;
#define HBE_THROW_F(code, format, ...) *hbe_err = hbe_err_create(code, format, ##__VA_ARGS__); goto finally;
#define HBE_CATCH(fn, ...) fn(hbe_err, ##__VA_ARGS__); HBE_COND return 0;
#define HBE_CATCH_V(fn, ...) fn(hbe_err, ##__VA_ARGS__); HBE_COND return;
#define HBE_CATCH_F(fn, ...) fn(hbe_err, ##__VA_ARGS__); HBE_COND goto finally;
#define HBE_PASS(fn, ...) fn(hbe_err, ##__VA_ARGS__); return 0;
#define HBE_PASS_V(fn, ...) fn(hbe_err, ##__VA_ARGS__); return;
#define HBE_PASS_F(fn, ...) fn(hbe_err, ##__VA_ARGS__); goto finally;
#define HB_RETURN_F(v) rv = v; goto finally;
#define MAX_ERR_MSG_LEN 1024
typedef enum hbe_errcode_e {
HBE_INTERR_UNKNOWN_ENTITY_TYPE = 2,
HBE_INTERR_UNKNOWN_CONTENT_NEXT_STATE,
HBE_INTERR_NOT_A_HB_DIR,
HBE_CLI_TOO_MANY_OPTIONS = 17,
HBE_CLI_INVALID_TAG_SET,
HBE_CLI_INVALID_TAG,
HBE_CLI_INVALID_SUPPRESSABLE_ERROR,
HBE_IO_FOPEN_FAIL = 33,
HBE_IO_FCLOSE_FAIL,
HBE_IO_FREAD_FAIL,
HBE_IO_FWRITE_FAIL,
HBE_PARSE_MALFORMED_ENTITY = 65,
HBE_PARSE_BARE_AMPERSAND,
HBE_PARSE_INVALID_ENTITY,
HBE_PARSE_NONSTANDARD_TAG,
HBE_PARSE_UCASE_TAG,
HBE_PARSE_UCASE_ATTR,
HBE_PARSE_UNQUOTED_ATTR,
HBE_PARSE_ILLEGAL_CHILD,
HBE_PARSE_UNCLOSED_TAG,
HBE_PARSE_SELF_CLOSING_TAG,
HBE_PARSE_NO_SPACE_BEFORE_ATTR,
HBE_PARSE_UNEXPECTED_END,
HBE_PARSE_EXPECTED_NOT_FOUND,
} hbe_errcode_t;
typedef struct hbe_err_s {
hbe_errcode_t code;
char *message;
} *hbe_err_t;
hbe_err_t hbe_err_create(hbe_errcode_t code, char *format, ...) {
va_list args;
va_start(args, format);
char *message = calloc(MAX_ERR_MSG_LEN + 1, SIZEOF_CHAR);
vsnprintf(message, MAX_ERR_MSG_LEN, format, args);
va_end(args);
hbe_err_t err = malloc(sizeof(struct hbe_err_s));
err->code = code;
err->message = message;
return err;
}
hbe_errcode_t hbe_err_code(hbe_err_t err) {
return err->code;
}
char *hbe_err_message(hbe_err_t err) {
return err->message;
}
void hbe_err_destroy(hbe_err_t err) {
free(err->message);
free(err);
}

View File

@ -1,51 +0,0 @@
#define PC_RED "\e[31m"
#define PC_GRN "\e[32m"
#define PC_YEL "\e[33m"
#define PC_BLU "\e[34m"
#define PC_MAG "\e[35m"
#define PC_CYN "\e[36m"
#define PC_WHT "\e[37m"
#define PC_RESET "\e[0m"
#define PC_BOLD "\e[1m"
#define HBL_LOG_DEBUG_PREFIX "[DEBUG] "
#define HBL_LOG_INFO_PREFIX PC_MAG "[INFO] "
#define HBL_LOG_WARN_PREFIX PC_BOLD PC_YEL "[WARN] "
#define HBL_KV_LOGLINE_LEFTWIDTH "35"
typedef enum hbl_log_level_e {
HBL_LOG_DEBUG,
HBL_LOG_INFO,
HBL_LOG_WARN,
} hbl_log_level_t;
void hbl_log(hbl_log_level_t level, char *fmt, ...) {
va_list args;
va_start(args, fmt);
const char *prefix = level == HBL_LOG_INFO ? (HBL_LOG_INFO_PREFIX) :
level == HBL_LOG_WARN ? (HBL_LOG_WARN_PREFIX) :
HBL_LOG_DEBUG_PREFIX;
fprintf(stderr, "%s" PC_RESET, prefix);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
}
void hbl_info_kv_boolean(char *name, int state) {
const char *color = state ? (PC_BOLD PC_GRN) : PC_MAG;
const char *label = state ? "ON" : "OFF";
hbl_log(HBL_LOG_INFO, "%-" HBL_KV_LOGLINE_LEFTWIDTH "s%s%s" PC_RESET, name, color, label);
}
void hbl_info_kv_string(char *name, char *value) {
hbl_log(HBL_LOG_INFO, "%-" HBL_KV_LOGLINE_LEFTWIDTH "s" PC_BOLD "%s" PC_RESET, name, value);
}
void hbl_error(hbe_err_t err) {
fprintf(stderr, PC_BOLD PC_RED "[FATAL]" PC_RESET PC_RED " Error %d: %s\n" PC_RESET, err->code, err->message);
}

View File

@ -1,45 +0,0 @@
#include <errno.h>
#include "../execution/error.c"
#define HBU_FSTREAM_BUILD_INFRA(type, mode, noun, verb, std) \
typedef struct hbu_fstream##type##_s \
{ \
const char *name; \
FILE *fd; \
} * hbu_fstream##type##_t; \
\
hbu_fstream##type##_t hbu_fstream##type##_create(hbe_err_t *hbe_err, char *path) \
{ \
hbu_fstream##type##_t fstream = malloc(sizeof(struct hbu_fstream##type##_s)); \
\
if (path == NULL) \
{ \
fstream->name = #std; \
fstream->fd = std; \
} \
else \
{ \
fstream->name = path; \
\
FILE *fd = fopen(path, mode); \
\
if (fd == NULL) \
{ \
HBE_THROW(HBE_IO_FOPEN_FAIL, "Failed to open file %s for " verb " with error %d", fstream->name, errno); \
} \
\
fstream->fd = fd; \
} \
\
return fstream; \
} \
\
void hbu_fstream##type##_destroy(hbe_err_t *hbe_err, hbu_fstream##type##_t fstream) \
{ \
if (fclose(fstream->fd) == EOF) \
{ \
HBE_THROW_V(HBE_IO_FCLOSE_FAIL, "Failed to close " noun " stream for file %s with error %d", fstream->name, errno); \
} \
\
free(fstream); \
}

View File

@ -1,21 +0,0 @@
#include <errno.h>
#include "../char/char.c"
#include "../execution/error.c"
#include "./__base__.c"
HBU_FSTREAM_BUILD_INFRA(in, "r", "read", "reading", stdin)
hb_eod_char_t hbu_fstreamin_read(hbe_err_t *hbe_err, hbu_fstreamin_t fstreamin) {
hb_char_t c;
if (fread(&c, SIZEOF_CHAR, 1, fstreamin->fd) != SIZEOF_CHAR) {
if (ferror(fstreamin->fd)) {
HBE_THROW(HBE_IO_FREAD_FAIL, "Failed to read input file %s", fstreamin->name);
}
// Must be EOF
return HB_EOD;
}
return c;
}

View File

@ -1,20 +0,0 @@
#include <errno.h>
#include "../execution/error.c"
#include "../list/char.c"
#include "./__base__.c"
HBU_FSTREAM_BUILD_INFRA(out, "w", "write", "writing", stdout)
static void _hbu_fstreamout_fwrite(hbe_err_t *hbe_err, hbu_fstreamout_t fstreamout, hb_char_t *source, size_t length) {
if (fwrite(source, SIZEOF_CHAR, length, fstreamout->fd) != SIZEOF_CHAR * length) {
HBE_THROW_V(HBE_IO_FWRITE_FAIL, "Failed to write to output file %s", fstreamout->name);
}
}
void hbu_fstreamout_write_buffer(hbe_err_t *hbe_err, hbu_fstreamout_t fstreamout, hbu_list_char_t buffer) {
HBE_CATCH_V(_hbu_fstreamout_fwrite, fstreamout, hbu_list_char_underlying(buffer), buffer->length);
}
void hbu_fstreamout_write(hbe_err_t *hbe_err, hbu_fstreamout_t fstreamout, hb_char_t c) {
HBE_CATCH_V(_hbu_fstreamout_fwrite, fstreamout, &c, 1);
}

View File

@ -1,237 +0,0 @@
#include <stddef.h>
#include <stdlib.h>
#define INITIAL_LIST_SIDE_SIZE 10
#define LIST_GROWTH_RATE 1.5
#define HBU_LIST(name, elem_type, elem_size, invsafe_elem_type, invalid_value) \
typedef struct name##_s \
{ \
elem_type *data; \
size_t head; \
size_t length; \
size_t size; \
size_t size_left; \
size_t size_right; \
} * name##_t; \
\
name##_t name##_create_size(size_t initial_size_left, size_t initial_size_right) \
{ \
size_t initial_size = initial_size_left + initial_size_right; \
name##_t buf = malloc(sizeof(struct name##_s)); \
buf->data = calloc(initial_size, elem_size); \
buf->head = initial_size_left; \
buf->length = 0; \
buf->size = initial_size; \
buf->size_left = initial_size_left; \
buf->size_right = initial_size_right; \
return buf; \
} \
\
name##_t name##_create(void) \
{ \
return name##_create_size(INITIAL_LIST_SIDE_SIZE, INITIAL_LIST_SIDE_SIZE); \
} \
\
void name##_destroy(name##_t buf) \
{ \
free(buf->data); \
free(buf); \
} \
\
void name##_destroy_shallow(name##_t buf) \
{ \
free(buf); \
} \
\
elem_type *name##_underlying(name##_t buf) \
{ \
return &((buf->data)[buf->head]); \
} \
\
elem_type *name##_underlying_copy(name##_t buf) \
{ \
elem_type *copy = calloc(buf->length + 1, elem_size); \
memcpy(copy, name##_underlying(buf), buf->length *elem_size); \
return copy; \
} \
\
int name##_valid_index(name##_t buf, size_t idx) \
{ \
return idx < buf->length; \
} \
\
invsafe_elem_type name##_get(name##_t buf, size_t idx) \
{ \
if (!name##_valid_index(buf, idx)) \
{ \
return invalid_value; \
} \
\
return buf->data[buf->head + idx]; \
} \
\
void name##_set(name##_t buf, size_t idx, elem_type c) \
{ \
if (!name##_valid_index(buf, idx)) \
{ \
return; \
} \
\
buf->data[buf->head + idx] = c; \
} \
\
void name##_size_expand_left(name##_t buf, size_t new_size_left) \
{ \
size_t old_size_left = buf->size_left; \
\
if (old_size_left >= new_size_left) \
{ \
return; \
} \
\
size_t diff_size_left = new_size_left - old_size_left; \
\
size_t new_size = new_size_left + buf->size_right; \
\
elem_type *new_data = calloc(new_size, elem_size); \
memcpy(&(new_data[diff_size_left]), buf->data, elem_size * buf->length); \
free(buf->data); \
\
buf->data = new_data; \
buf->head += old_size_left; \
buf->size = new_size; \
buf->size_left = new_size_left; \
} \
\
void name##_size_expand_right(name##_t buf, size_t new_size_right) \
{ \
size_t old_size = buf->size; \
size_t old_size_right = buf->size_right; \
\
if (old_size_right >= new_size_right) \
{ \
return; \
} \
\
size_t new_size = buf->size_left + new_size_right; \
\
elem_type *new_data = realloc(buf->data, elem_size * new_size); \
for (size_t i = old_size; i < new_size; i++) \
{ \
new_data[i] = 0; \
} \
\
buf->data = new_data; \
buf->size = new_size; \
buf->size_right = new_size_right; \
} \
\
void name##_ensure_left(name##_t buf, size_t amount) \
{ \
size_t desired_size = amount + 1; \
\
if (buf->size_left < desired_size) \
{ \
name##_size_expand_left(buf, desired_size); \
} \
} \
\
void name##_ensure_right(name##_t buf, size_t amount) \
{ \
size_t desired_size = amount + 1; \
\
if (buf->size_right < desired_size) \
{ \
name##_size_expand_right(buf, desired_size); \
} \
} \
\
void name##_append(name##_t buf, elem_type tail) \
{ \
size_t next_idx = buf->head + buf->length; \
\
if (next_idx >= buf->size - 1) \
{ \
size_t old_size = buf->size_right; \
size_t new_size = old_size * LIST_GROWTH_RATE + 1; /* +1 to always guarantee an increase */ \
\
name##_size_expand_right(buf, new_size); \
} \
\
buf->data[next_idx] = tail; \
buf->length++; \
} \
\
void name##_extend_arr(name##_t buf, elem_type *ext, size_t ext_len) \
{ \
name##_ensure_right(buf, buf->length + ext_len); \
for (size_t i = 0; i < ext_len; i++) \
{ \
name##_append(buf, ext[i]); \
} \
} \
\
void name##_extend_buf(name##_t buf, name##_t ext) \
{ \
name##_ensure_right(buf, buf->length + ext->length); \
for (size_t i = 0; i < buf->length; i++) \
{ \
name##_append(buf, buf->data[i]); \
} \
} \
\
invsafe_elem_type name##_shift(name##_t buf) \
{ \
if (buf->length == 0) \
{ \
return invalid_value; \
} \
\
elem_type f = buf->data[buf->head]; \
buf->data[buf->head] = 0; \
buf->head++; \
buf->length--; \
\
return f; \
} \
\
void name##_unshift(name##_t buf, elem_type head) \
{ \
if (buf->head == 0) \
{ \
size_t old_size = buf->size_left; \
size_t new_size = old_size * LIST_GROWTH_RATE + 1; /* +1 to always guarantee an increase */ \
\
name##_size_expand_left(buf, new_size); \
} \
\
buf->head--; \
buf->data[buf->head] = head; \
buf->length++; \
} \
\
invsafe_elem_type name##_pop(name##_t buf) \
{ \
if (buf->length == 0) \
{ \
return invalid_value; \
} \
\
size_t idx = buf->head + buf->length - 1; \
\
elem_type l = buf->data[idx]; \
\
buf->data[idx] = 0; \
buf->length--; \
\
return l; \
} \
\
int name##_compare(name##_t a, name##_t b); \
\
int name##_equal(name##_t a, name##_t b) \
{ \
return a->length == b->length && \
name##_compare(a, b) == 0; \
}

View File

@ -1,14 +0,0 @@
#include <string.h>
#include "../char/char.c"
#include "./__base__.c"
HBU_LIST(hbu_list_char, hb_char_t, SIZEOF_CHAR, hb_eod_char_t, HB_EOD);
int hbu_list_char_compare(hbu_list_char_t a, hbu_list_char_t b) {
// All buffers have NULL-terminated underlying char arrays
return strcmp((char *) hbu_list_char_underlying(a), (char *) hbu_list_char_underlying(b));
}
int hbu_list_char_compare_lit(hbu_list_char_t a, const char *b) {
return strcmp((char *) hbu_list_char_underlying(a), b);
}

View File

@ -1,68 +0,0 @@
#include "../math/math.c"
#include "./__base__.c"
#include "./char.c"
HBU_LIST(hbu_list_charlist, hbu_list_char_t, sizeof(hbu_list_char_t), hbu_list_char_t, NULL);
int hbu_list_charlist_compare(hbu_list_charlist_t a, hbu_list_charlist_t b)
{
size_t max = hbu_max(a->length, b->length);
for (size_t i = 0; i < max; i++)
{
hbu_list_char_t a1 = hbu_list_charlist_get(a, i);
hbu_list_char_t b1 = hbu_list_charlist_get(b, i);
if (a1 == NULL)
{
return -1;
}
if (b1 == NULL)
{
return 1;
}
int subcmp = hbu_list_char_compare(a1, b1);
if (subcmp != 0)
{
return subcmp;
}
}
return 0;
}
hbu_list_charlist_t hbu_list_charlist_create_from_split(hb_char_t *source, hb_char_t delim)
{
hbu_list_charlist_t parts = hbu_list_charlist_create();
hbu_list_char_t part = hbu_list_char_create();
hbu_list_charlist_append(parts, part);
hb_char_t c;
size_t i = 0;
while ((c = source[i]))
{
if (c == delim)
{
part = hbu_list_char_create();
hbu_list_charlist_append(parts, part);
}
else
{
hbu_list_char_append(part, c);
}
i++;
}
return parts;
}
void hbu_list_charlist_destroy_from_split(hbu_list_charlist_t list)
{
for (size_t i = 0; i < list->length; i++) {
hbu_list_char_destroy(hbu_list_charlist_get(list, i));
}
hbu_list_charlist_destroy(list);
}

View File

@ -1 +0,0 @@
NICEHASH_MAP_STR(int32, int32_t)

View File

@ -1 +0,0 @@
NICEHASH_MAP_STR(strset, nh_set_str_t)

View File

@ -1,4 +0,0 @@
#define hbu_max(a, b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })

View File

@ -1,954 +0,0 @@
#include <string.h>
#include "../../rule/char/ucalpha.c"
#include "../char/char.c"
#include "../execution/error.c"
#include "../list/char.c"
#include "../fstream/fstreamin.c"
#include "../fstream/fstreamout.c"
#define HBU_PIPE_MAX_ERR_MSG_LEN 1024
// Use macro to prevent having to allocate (and therefore free/manage) memory
#define HBU_FN_FORMAT_WITH_POS(fn, a, format, ...) fn(a, format " at %s [line %d, column %d]", __VA_ARGS__, pipe->input_name, pipe->line, pipe->column);
#define HBU_PIPE_THROW(pipe, errcode, format, ...) *hbe_err = hbu_pipe_error(pipe, errcode, format, ##__VA_ARGS__); return 0;
#define HBU_PIPE_THROW_V(pipe, errcode, format, ...) *hbe_err = hbu_pipe_error(pipe, errcode, format, ##__VA_ARGS__); return;
#define HBU_PIPE_THROW_F(pipe, errcode, format, ...) *hbe_err = hbu_pipe_error(pipe, errcode, format, ##__VA_ARGS__); goto finally;
typedef int (*hbu_pipe_predicate_t)(hb_char_t);
typedef hb_eod_char_t (*hbu_pipe_reader_cb_t)(hbe_err_t *hbe_err, void *);
typedef void (*hbu_pipe_writer_cb_t)(hbe_err_t *hbe_err, void *, hb_char_t);
typedef struct hbu_pipe_s {
void* input;
hbu_pipe_reader_cb_t reader;
void *output;
hbu_pipe_writer_cb_t writer;
char *input_name;
int output_masked;
hbu_list_char_t output_redirect;
hbu_list_char_t buffer;
int line;
int column;
int lastCharWasCR;
int EOI;
} *hbu_pipe_t;
/*
*
* MESSAGING
*
*/
/**
* Logs a debug message with the current position appended.
*
* @param pipe pipe
* @param msg message
*/
void hbu_pipe_debug(hbu_pipe_t pipe, const char *msg) {
HBU_FN_FORMAT_WITH_POS(hbl_log, HBL_LOG_DEBUG, "%s", msg);
}
/**
* Logs a warning message with the current position appended.
*
* @param pipe pipe
* @param msg message
*/
void hbu_pipe_warn(hbu_pipe_t pipe, const char *msg) {
HBU_FN_FORMAT_WITH_POS(hbl_log, HBL_LOG_WARN, "%s", msg);
}
/**
* Creates an error using a message with the current position appended.
*
* @param pipe pipe
* @param errcode error code
* @param reason message
* @return error
*/
hbe_err_t hbu_pipe_error(hbu_pipe_t pipe, hbe_errcode_t errcode, const char *reason, ...) {
va_list args;
va_start(args, reason);
char *msg = calloc(HBU_PIPE_MAX_ERR_MSG_LEN + 1, SIZEOF_CHAR);
vsnprintf(msg, HBU_PIPE_MAX_ERR_MSG_LEN, reason, args);
va_end(args);
hbe_err_t err = HBU_FN_FORMAT_WITH_POS(hbe_err_create, errcode, "%s", msg);
free(msg);
return err;
}
/*
*
* INTERNAL FUNCTIONS
*
*/
/**
* Reads from input.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @throws on read error
*/
static hb_eod_char_t _hbu_pipe_read_from_input(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
hb_eod_char_t c = HBE_CATCH((*pipe->reader), pipe->input);
if (c == HB_EOD) {
pipe->EOI = 1;
}
return c;
}
/**
* Ensures the buffer has loaded at least the next <code>offset</code> characters, or the remaining
* characters from input if there are less than <code>offset</code> characters left.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @throws on read error
*/
static void _hbu_pipe_ensure_buffer(hbe_err_t *hbe_err, hbu_pipe_t pipe, size_t offset) {
if (pipe->EOI) {
return;
}
size_t current = pipe->buffer->length;
if (offset <= current) {
return;
}
size_t gap = offset - current;
for (size_t i = 0; i < gap; i++) {
hb_eod_char_t c = HBE_CATCH_V(_hbu_pipe_read_from_input, pipe);
if (c == HB_EOD) {
// EOI flag already set by _hbu_pipe_read_from_input
return;
}
hbu_list_char_append(pipe->buffer, c);
}
}
/**
* Gets the next character, whether it's by shifting the buffer or reading from input.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @throws on read error
*/
static hb_eod_char_t _hbu_pipe_read_from_buffer_or_input(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
if (pipe->EOI) {
return HB_EOD;
}
if (pipe->buffer->length) {
return hbu_list_char_shift(pipe->buffer);
}
return HBE_CATCH(_hbu_pipe_read_from_input, pipe);
}
static void _hbu_pipe_update_pos(hbu_pipe_t pipe, hb_char_t c) {
switch (c) {
case '\r':
pipe->lastCharWasCR = 1;
pipe->line++;
pipe->column = 0;
break;
case '\n':
if (!pipe->lastCharWasCR) {
pipe->line++;
pipe->column = 0;
} else {
pipe->lastCharWasCR = 0;
}
break;
default:
pipe->column++;
pipe->lastCharWasCR = 0;
}
}
/**
* Ensures that the provided character is not @{link HB_EOD}, and causes an error otherwise.
*
* @param c character to test for <code>HB_EOD</code>
* @throws HBE_PARSE_UNEXPECTED_END if <code>c</code> is <code>HB_EOD</code>
*/
static void _hbu_pipe_assert_not_eoi(hbe_err_t *hbe_err, hb_eod_char_t c) {
if (c == HB_EOD) {
HBE_THROW_V(HBE_PARSE_UNEXPECTED_END, "Unexpected end of input");
}
}
/**
* Writes a character to the redirect, if enabled, otherwise output, of a pipe,
* unless the output is masked.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param c character to write
* @return a freshly-created pipe
* @throws on write error
*/
static void _hbu_pipe_write_to_output(hbe_err_t *hbe_err, hbu_pipe_t pipe, hb_char_t c) {
if (!pipe->output_masked) {
hbu_list_char_t redirect = pipe->output_redirect;
if (redirect != NULL) {
hbu_list_char_append(redirect, c);
} else {
HBE_CATCH_V((*pipe->writer), pipe->output, c);
}
}
}
/*
*
* INSTANCE MANAGEMENT FUNCTIONS
*
*/
/**
* Allocates memory for a pipe, and creates one with provided arguments.
*
* @param input input
* @param reader reader
* @param input_name input_name
* @param output output
* @param writer writer
* @return a freshly-created pipe
*/
hbu_pipe_t hbu_pipe_create(void *input, hbu_pipe_reader_cb_t reader, char *input_name, void *output, hbu_pipe_writer_cb_t writer) {
hbu_pipe_t pipe = malloc(sizeof(struct hbu_pipe_s));
pipe->input = input;
pipe->reader = reader;
pipe->output = output;
pipe->writer = writer;
pipe->input_name = input_name;
pipe->output_masked = 0;
pipe->output_redirect = NULL;
pipe->buffer = hbu_list_char_create();
pipe->line = 1;
pipe->column = 0;
pipe->lastCharWasCR = 0;
pipe->EOI = 0; // End Of Input
return pipe;
}
/**
* Frees all memory associated with a pipe.
*
* @param pipe pipe
*/
void hbu_pipe_destroy(hbu_pipe_t pipe) {
hbu_list_char_destroy(pipe->buffer);
free(pipe);
}
/**
* Enables or disables the output mask.
* When the output mask is enabled, all writes are simply discarded and not actually written to output.
*
* @param pipe pipe
* @param output_masked 1 to enable, 0 to disable
* @return previous state
*/
int hbu_pipe_toggle_output_mask(hbu_pipe_t pipe, int output_masked) {
int current = pipe->output_masked;
pipe->output_masked = output_masked;
return current;
}
/**
* Enables or disables the output redirect.
* When the output redirect is enabled, all writes are written to a buffer instead of the output.
*
* @param pipe pipe
* @param output_redirect buffer to redirect writes to, or NULL to disable
*/
void hbu_pipe_set_output_redirect(hbu_pipe_t pipe, hbu_list_char_t output_redirect) {
pipe->output_redirect = output_redirect;
}
/*
*
* BUILDER FUNCTIONS
*
*/
hbu_pipe_t hbu_pipe_create_blank(char *input_name) {
return hbu_pipe_create(NULL, NULL, input_name, NULL, NULL);
}
void hbu_pipe_blank_set_input_fstreamin(hbu_pipe_t pipe, hbu_fstreamin_t fstreamin) {
pipe->input = fstreamin;
pipe->reader = (hbu_pipe_reader_cb_t) &hbu_fstreamin_read;
}
// Wrapper function for hbu_list_char_shift to make it compatible with hbu_pipe_reader_cb_t
hb_eod_char_t hbu_pipe_read_from_list_char_input(hbe_err_t *hbe_err, hbu_list_char_t input) {
(void) hbe_err;
return hbu_list_char_shift(input);
}
void hbu_pipe_blank_set_input_buffer(hbu_pipe_t pipe, hbu_list_char_t buf) {
pipe->input = buf;
pipe->reader = (hbu_pipe_reader_cb_t) &hbu_pipe_read_from_list_char_input;
}
void hbu_pipe_blank_set_output_fstreamout(hbu_pipe_t pipe, hbu_fstreamout_t fstreamout) {
pipe->output = fstreamout;
pipe->writer = (hbu_pipe_writer_cb_t) &hbu_fstreamout_write;
}
// Wrapper function for hbu_list_char_append to make it compatible with hbu_pipe_writer_cb_t
void hbu_pipe_write_to_list_char_output(hbe_err_t *hbe_err, hbu_list_char_t output, hb_char_t c) {
(void) hbe_err;
hbu_list_char_append(output, c);
}
void hbu_pipe_blank_set_output_buffer(hbu_pipe_t pipe, hbu_list_char_t buf) {
pipe->output = buf;
pipe->writer = (hbu_pipe_writer_cb_t) &hbu_pipe_write_to_list_char_output;
}
/*
*
* CONSUMERS AND PRODUCERS
*
*/
/**
* Gets the next character.
* If it's the end, {@link HB_EOD} is returned.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return character or {@link HB_EOD}
* @throws on read error
*/
hb_eod_char_t hbu_pipe_peek_eoi(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
// OPTIMISATION: Read directly from buffer if available (no need to unshift later)
if (pipe->buffer->length) {
return hbu_list_char_get(pipe->buffer, 0);
}
hb_eod_char_t c = HBE_CATCH(_hbu_pipe_read_from_buffer_or_input, pipe);
if (c != HB_EOD) {
hbu_list_char_unshift(pipe->buffer, c);
}
return c;
}
/**
* Gets the next character.
* Will cause an error if it's the end and there is no next character.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return character
* @throws on read error or HBE_PARSE_UNEXPECTED_END
*/
hb_char_t hbu_pipe_peek(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
hb_eod_char_t c = HBE_CATCH(hbu_pipe_peek_eoi, pipe);
HBE_CATCH(_hbu_pipe_assert_not_eoi, c);
return c;
}
/**
* Gets the <i>n</i>th character from current, where <i>n</i> is <code>offset</code>.
* When <code>offset</code> is 1, the next character is returned (equivalent to {@link hbu_pipe_peek_eoi_offset}).
* If <code>offset</code> is after the last character, {@link HB_EOD} is returned.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param offset position of character to get
* @return character or {@link HB_EOD}
* @throws on read error
*/
hb_eod_char_t hbu_pipe_peek_eoi_offset(hbe_err_t *hbe_err, hbu_pipe_t pipe, size_t offset) {
HBE_CATCH(_hbu_pipe_ensure_buffer, pipe, offset);
hb_eod_char_t c = hbu_list_char_get(pipe->buffer, offset - 1);
return c;
}
/**
* Gets the <i>n</i>th character from current, where <i>n</i> is <code>offset</code>.
* When <code>offset</code> is 1, the next character is returned (equivalent to {@link hbu_pipe_peek_offset}).
* An error will be caused if <code>offset</code> is after the last character.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param offset position of character to get
* @return character
* @throws on read error or HBE_PARSE_UNEXPECTED_END
*/
hb_char_t hbu_pipe_peek_offset(hbe_err_t *hbe_err, hbu_pipe_t pipe, size_t offset) {
hb_eod_char_t c = HBE_CATCH(hbu_pipe_peek_eoi_offset, pipe, offset);
HBE_CATCH(_hbu_pipe_assert_not_eoi, c);
return c;
}
/**
* Checks if the next sequence of characters is the null-terminated character array <code>match</code>.
* Won't cause an error if insufficient amount of characters left.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match characters to match
* @return amount of characters matched, which should be equal to <code>strlen(match)</code>
* @throws on read error
*/
size_t hbu_pipe_matches(hbe_err_t *hbe_err, hbu_pipe_t pipe, const char *match) {
size_t matchlen = strlen(match);
for (size_t i = 0; i < matchlen; i++) {
hb_eod_char_t c = HBE_CATCH(hbu_pipe_peek_eoi_offset, pipe, i + 1);
if (c == HB_EOD || c != match[i]) {
return 0;
}
}
return matchlen;
}
/**
* Checks if the next sequence of characters matches the null-terminated character array <code>match</code>
* of lowercase characters case-insensitively.
* Won't cause an error if insufficient amount of characters left.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match characters to match case-insensitively
* @return amount of characters matched, which should be equal to <code>strlen(match)</code>
* @throws on read error
*/
size_t hbu_pipe_matches_i(hbe_err_t *hbe_err, hbu_pipe_t pipe, const char *match) {
size_t matchlen = strlen(match);
for (size_t i = 0; i < matchlen; i++) {
hb_eod_char_t c = HBE_CATCH(hbu_pipe_peek_eoi_offset, pipe, i + 1);
if (!(
c != HB_EOD &&
(
c == match[i] ||
(hbr_ucalpha_check(c) && (c + 32) == match[i])
)
)) {
return 0;
}
}
return matchlen;
}
/**
* Checks if the next sequence of characters is "\r", "\n", or "\r\n".
* Won't cause an error if insufficient amount of characters left.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return amount of characters matched
* @throws on read error
*/
size_t hbu_pipe_matches_line_terminator(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
// Can't chain into HBE_CATCH(...) || HBE_CATCH(...) || ...
// as HBE_CATCH needs auxiliary statement
// `\r\n` must be before `\r`
size_t crlf = HBE_CATCH(hbu_pipe_matches, pipe, "\r\n");
if (crlf) return crlf;
size_t cr = HBE_CATCH(hbu_pipe_matches, pipe, "\r");
if (cr) return cr;
return HBE_CATCH(hbu_pipe_matches, pipe, "\n");
}
/**
* Accepts the next character.
* Will cause an error if already at end.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return next character
* @throws on read/write error or HBE_PARSE_UNEXPECTED_END
*/
hb_char_t hbu_pipe_accept(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
hb_eod_char_t c = HBE_CATCH(_hbu_pipe_read_from_buffer_or_input, pipe);
HBE_CATCH(_hbu_pipe_assert_not_eoi, c);
_hbu_pipe_update_pos(pipe, c);
HBE_CATCH(_hbu_pipe_write_to_output, pipe, c);
return c;
}
/**
* Accepts the next <code>count</code> characters.
* Requires at least <code>count</code> characters remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param count amount of characters
* @throws on read/write error or HBE_PARSE_UNEXPECTED_END
*/
void hbu_pipe_accept_count(hbe_err_t *hbe_err, hbu_pipe_t pipe, size_t count) {
for (size_t i = 0; i < count; i++) {
HBE_CATCH_V(hbu_pipe_accept, pipe);
}
}
/**
* Accepts the following character if it is <code>c</code>.
* Won't match or cause an error if there are no characters remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param c character to match
* @return 0 if nothing was accepted, 1 otherwise
* @throws on read/write error
*/
int hbu_pipe_accept_if(hbe_err_t *hbe_err, hbu_pipe_t pipe, hb_char_t c) {
hb_eod_char_t n = HBE_CATCH(hbu_pipe_peek_eoi, pipe);
if (n == HB_EOD || n != c) {
return 0;
}
HBE_CATCH(hbu_pipe_accept, pipe);
return 1;
}
/**
* Accepts the following characters if they match <code>match</code>.
* Won't match or cause an error if there are not enough characters remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match characters to match
* @return 0 if nothing was accepted, 1 otherwise
* @throws on read/write error
*/
int hbu_pipe_accept_if_matches(hbe_err_t *hbe_err, hbu_pipe_t pipe, const char *match) {
size_t matchedlen = HBE_CATCH(hbu_pipe_matches, pipe, match);
int matched = matchedlen > 0;
if (matched) {
HBE_CATCH(hbu_pipe_accept_count, pipe, matchedlen);
}
return matched;
}
/**
* Accepts the following characters if they are either "\r", "\r\n", or "\n".
* Won't cause an error if insufficient amount of characters left.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return amount of characters matched
* @throws on read/write error
*/
size_t hbu_pipe_accept_if_matches_line_terminator(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
size_t matchedlen = HBE_CATCH(hbu_pipe_matches_line_terminator, pipe);
if (matchedlen) {
HBE_CATCH(hbu_pipe_accept_count, pipe, matchedlen);
}
return matchedlen;
}
/**
* Accepts the following character if it satisfies the predicate <code>pred</code>.
* Won't do anything if already at the end.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param pred predicate
* @return 0 if nothing was accepted, 1 otherwise
* @throws on read/write error
*/
int hbu_pipe_accept_if_predicate(hbe_err_t *hbe_err, hbu_pipe_t pipe, hbu_pipe_predicate_t pred) {
hb_eod_char_t c = HBE_CATCH(hbu_pipe_peek_eoi, pipe);
if (c == HB_EOD || !(*pred)(c)) {
return 0;
}
HBE_CATCH(hbu_pipe_accept, pipe);
return 1;
}
/**
* Accepts every following character until one dissatisfies the predicate <code>pred</code>,
* or the end is reached.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param pred predicate
* @return amount of characters accepted
* @throws on read/write error
*/
size_t hbu_pipe_accept_while_predicate(hbe_err_t *hbe_err, hbu_pipe_t pipe, hbu_pipe_predicate_t pred) {
size_t count = 0;
while (1) {
int matched = HBE_CATCH(hbu_pipe_accept_if_predicate, pipe, pred);
if (!matched) {
break;
}
count++;
}
return count;
}
/**
* Skips over the next character.
* Requires that the file has at least one character remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @return skipped character
* @throws on read error or HBE_PARSE_UNEXPECTED_END
*/
hb_char_t hbu_pipe_skip(hbe_err_t *hbe_err, hbu_pipe_t pipe) {
hb_eod_char_t c = HBE_CATCH(_hbu_pipe_read_from_buffer_or_input, pipe);
HBE_CATCH(_hbu_pipe_assert_not_eoi, c);
_hbu_pipe_update_pos(pipe, c);
return c;
}
/**
* Skips over the next <code>amount</code> characters.
* Requires that the file has at least <code>amount</code> characters remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param amount amount of characters to skip
* @throws on read error or HBE_PARSE_UNEXPECTED_END
*/
void hbu_pipe_skip_amount(hbe_err_t *hbe_err, hbu_pipe_t pipe, size_t amount) {
for (size_t i = 0; i < amount; i++) {
HBE_CATCH_V(hbu_pipe_skip, pipe);
}
}
/**
* Skips over the following character if it is <code>c</code>.
* Won't cause an error if the end is reached.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param c character to skip if next
* @return 1 if skipped, 0 otherwise
* @throws on read error
*/
int hbu_pipe_skip_if(hbe_err_t *hbe_err, hbu_pipe_t pipe, hb_char_t c) {
hb_eod_char_t n = HBE_CATCH(hbu_pipe_peek_eoi, pipe);
if (n == HB_EOD || n != c) {
return 0;
}
HBE_CATCH(hbu_pipe_skip, pipe);
return 1;
}
/**
* Skips over every following character until one dissatisfies the predicate <code>pred</code>,
* or the end is reached.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param pred predicate
* @return amount of characters skipped
* @throws on read error
*/
size_t hbu_pipe_skip_while_predicate(hbe_err_t *hbe_err, hbu_pipe_t pipe, hbu_pipe_predicate_t pred) {
size_t count = 0;
while (1) {
hb_eod_char_t c = HBE_CATCH(hbu_pipe_peek_eoi, pipe);
if (c == HB_EOD || !(*pred)(c)) {
break;
}
HBE_CATCH(hbu_pipe_skip, pipe);
count++;
}
return count;
}
/**
* Skips over the next sequence of characters if they are <code>match</code>.
* Requires that the file has at least the length of <code>match</code> characters remaining.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match sequence of characters to match
* @return 0 if not matched, 1 otherwise
* @throws on read error or HBE_PARSE_UNEXPECTED_END
*/
int hbu_pipe_skip_if_matches(hbe_err_t *hbe_err, hbu_pipe_t pipe, const char *match) {
size_t matchedlen = HBE_CATCH(hbu_pipe_matches, pipe, match);
int matched = matchedlen > 0;
if (matched) {
HBE_CATCH(hbu_pipe_skip_amount, pipe, matchedlen);
}
return matched;
}
/**
* Requires the next character to be <code>c</code>.
* The matched character is written to output.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param c character to match
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
void hbu_pipe_require(hbe_err_t *hbe_err, hbu_pipe_t pipe, hb_char_t c) {
hb_char_t n = HBE_CATCH_V(hbu_pipe_accept, pipe);
if (c != n) {
HBU_PIPE_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected `%c` (0x%x), got `%c` (0x%x)", c, c, n, n);
}
}
/**
* Requires the next character to be <code>c</code>.
* The matched character is skipped over and NOT written to output, and also returned.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param c character to match
* @return matched character
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
hb_char_t hbu_pipe_require_skip(hbe_err_t *hbe_err, hbu_pipe_t pipe, hb_char_t c) {
hb_char_t n = HBE_CATCH(hbu_pipe_skip, pipe);
if (c != n) {
HBU_PIPE_THROW(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected `%c` (0x%x), got `%c` (0x%x) at %s", c, c, n, n);
}
return n;
}
/**
* Requires the next character to satisfy the predicate <code>pred</code>.
* The matched character is written to output.
* If not matched, the error message will describe the expected output using <code>name</code>.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param pred predicate
* @param name what to output in the error message to describe the requirement
* @return required character
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
hb_char_t hbu_pipe_require_predicate(hbe_err_t *hbe_err, hbu_pipe_t pipe, hbu_pipe_predicate_t pred, const char *name) {
hb_char_t n = HBE_CATCH(hbu_pipe_accept, pipe);
if (!(*pred)(n)) {
HBU_PIPE_THROW(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected %s, got `%c` (0x%x)", name, n, n);
}
return n;
}
/**
* Requires the next character to satisfy the predicate <code>pred</code>.
* The matched character is skipped over and NOT written to output.
* If not matched, the error message will describe the expected output using <code>name</code>.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param pred predicate
* @param name what to output in the error message to describe the requirement
* @return required character
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
hb_char_t hbu_pipe_require_skip_predicate(hbe_err_t *hbe_err, hbu_pipe_t pipe, hbu_pipe_predicate_t pred, const char *name) {
hb_char_t n = HBE_CATCH(hbu_pipe_skip, pipe);
if (!(*pred)(n)) {
HBU_PIPE_THROW(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected %s, got `%c` (0x%x)", name, n, n);
}
return n;
}
/**
* Requires the next sequence of characters to be equal to <code>match</code>.
* Matched characters are written to output.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match sequence of characters to require
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
void hbu_pipe_require_match(hbe_err_t *hbe_err, hbu_pipe_t pipe, const char *match) {
int matches = HBE_CATCH_V(hbu_pipe_accept_if_matches, pipe, match);
if (!matches) {
HBU_PIPE_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected `%s`", match);
}
}
/**
* Requires the next sequence of characters to be equal to <code>match</code>.
* Matched characters are skipped over and NOT written to output.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param match sequence of characters to require
* @throws on read/write error, HBE_PARSE_UNEXPECTED_END, or HBE_PARSE_EXPECTED_NOT_FOUND
*/
void hbu_pipe_require_skip_match(hbe_err_t *hbe_err, hbu_pipe_t pipe, const char *match) {
int matches = HBE_CATCH_V(hbu_pipe_skip_if_matches, pipe, match);
if (!matches) {
HBU_PIPE_THROW_V(pipe, HBE_PARSE_EXPECTED_NOT_FOUND, "Expected `%s`", match);
}
}
/**
* Writes a character.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param c character
* @throws on write error
*/
void hbu_pipe_write(hbe_err_t *hbe_err, hbu_pipe_t pipe, hb_char_t c) {
HBE_CATCH_V(_hbu_pipe_write_to_output, pipe, c);
}
/**
* Writes a character array until a NUL character is reached.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param str null-terminated character array
* @throws on write error
*/
void hbu_pipe_write_str(hbe_err_t *hbe_err, hbu_pipe_t pipe, hb_char_t *str) {
hb_char_t c;
for (size_t i = 0; (c = str[i]); i++) {
HBE_CATCH_V(_hbu_pipe_write_to_output, pipe, c);
}
}
/**
* Writes <code>len</code> characters from the beginning of a character array.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param str character array
* @param len amount of characters to write
* @throws on write error
*/
void hbu_pipe_write_str_len(hbe_err_t *hbe_err, hbu_pipe_t pipe, hb_char_t *str, size_t len) {
for (size_t i = 0; i < len; i++) {
HBE_CATCH_V(_hbu_pipe_write_to_output, pipe, str[i]);
}
}
/**
* Writes a buffer.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param buffer buffer
* @throws on write error
*/
void hbu_pipe_write_buffer(hbe_err_t *hbe_err, hbu_pipe_t pipe, hbu_list_char_t buffer) {
for (size_t i = 0; i < buffer->length; i++) {
HBE_CATCH_V(_hbu_pipe_write_to_output, pipe, hbu_list_char_get(buffer, i));
}
}
/**
* Writes a character from its Unicode code point in UTF-8 encoding, which may be multiple bytes.
*
* @param hbe_err pointer to hbe_err_t
* @param pipe pipe
* @param code_point Unicode code point
* @return amount of bytes written (0 if invalid code point)
* @throws on write error
*/
// Logic copied from https://gist.github.com/MightyPork/52eda3e5677b4b03524e40c9f0ab1da5
int hbu_pipe_write_unicode(hbe_err_t *hbe_err, hbu_pipe_t pipe, uint32_t code_point) {
if (code_point <= 0x7F) {
// Plain ASCII
HBE_CATCH(hbu_pipe_write, pipe, (hb_char_t) code_point);
return 1;
}
if (code_point <= 0x07FF) {
// 2-byte unicode
HBE_CATCH(hbu_pipe_write, pipe, (hb_char_t) (((code_point >> 6) & 0x1F) | 0xC0));
HBE_CATCH(hbu_pipe_write, pipe, (hb_char_t) (((code_point >> 0) & 0x3F) | 0x80));
return 2;
}
if (code_point <= 0xFFFF) {
// 3-byte unicode
HBE_CATCH(hbu_pipe_write, pipe, (hb_char_t) (((code_point >> 12) & 0x0F) | 0xE0));
HBE_CATCH(hbu_pipe_write, pipe, (hb_char_t) (((code_point >> 6) & 0x3F) | 0x80));
HBE_CATCH(hbu_pipe_write, pipe, (hb_char_t) (((code_point >> 0) & 0x3F) | 0x80));
return 3;
}
if (code_point <= 0x10FFFF) {
// 4-byte unicode
HBE_CATCH(hbu_pipe_write, pipe, (hb_char_t) (((code_point >> 18) & 0x07) | 0xF0));
HBE_CATCH(hbu_pipe_write, pipe, (hb_char_t) (((code_point >> 12) & 0x3F) | 0x80));
HBE_CATCH(hbu_pipe_write, pipe, (hb_char_t) (((code_point >> 6) & 0x3F) | 0x80));
HBE_CATCH(hbu_pipe_write, pipe, (hb_char_t) (((code_point >> 0) & 0x3F) | 0x80));
return 4;
}
return 0;
}