From 8da223461d14312a3f22df08f48d5a95d84fe721 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Mon, 18 Nov 2024 17:07:07 +0900 Subject: [PATCH] Impl. extension of template delimeter expressions Implemented "IF", "ELSEIF", "ELSE", "ENDIF", and "INDEX" for templates. IF is used like: `{{{!IF Variable==SomeString}}}`. ELSEIF is used like: `{{{!ELSEIF Variable==AnotherString}}}`. ELSE is used like: `{{{!ELSE}}}`. ENDIF is used like: `{{{!ENDIF}}}`. INDEX is used like: `{{{!INDEX ArrayVar[2]}}}`. --- example_config/example.config | 18 ++ src/helpers.c | 100 +++++- src/helpers.h | 17 ++ src/http_template.c | 555 +++++++++++++++++++++++++++++----- src/test.c | 40 +++ 5 files changed, 654 insertions(+), 76 deletions(-) diff --git a/example_config/example.config b/example_config/example.config index 2754095..454fc39 100644 --- a/example_config/example.config +++ b/example_config/example.config @@ -28,10 +28,28 @@ HTML='''

Test HTML


{{{Var}}}

To inner.

+

Tests:


+ {{{!IF ThisValue==false}}} + ThisValue is false. + {{{!ELSEIF ThisValue==true}}} + ThisValue is true. + {{{!ELSE}}} + ThisValue is invalid. + {{{!ENDIF}}} + +

Array Value:


+ {{{!INDEX ArrayValue[2]}}} + {{{!INDEX ArrayValue[0]}}} + {{{!INDEX ArrayValue[1]}}} ''' Var='''Test var value''' +ThisValue=derp + +ArrayValue=One_IDX_Zero +ArrayValue=Two_IDX_One +ArrayValue=Three_IDX_Two PATH=/inner HTML_FILE='''example_config/inner.html''' diff --git a/src/helpers.c b/src/helpers.c index efad799..7abbaee 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -17,6 +17,7 @@ #include "helpers.h" // Standard library includes. +#include #include #include #include @@ -31,7 +32,9 @@ int c_simple_http_internal_get_string_part_full_size(void *data, void *ud) { C_SIMPLE_HTTP_String_Part *part = data; size_t *count = ud; - *count += part->size - 1; + if (part->size > 0) { + *count += part->size - 1; + } return 0; } @@ -86,9 +89,36 @@ void c_simple_http_add_string_part( C_SIMPLE_HTTP_String_Part *string_part = malloc(sizeof(C_SIMPLE_HTTP_String_Part)); - string_part->size = strlen(c_string) + 1; - string_part->buf = malloc(string_part->size); - memcpy(string_part->buf, c_string, string_part->size); + if (c_string) { + string_part->size = strlen(c_string) + 1; + string_part->buf = malloc(string_part->size); + memcpy(string_part->buf, c_string, string_part->size); + } else { + string_part->size = 0; + string_part->buf = NULL; + } + + string_part->extra = extra; + simple_archiver_list_add( + list, string_part, c_simple_http_cleanup_string_part); +} + +void c_simple_http_add_string_part_sized( + SDArchiverLinkedList *list, + const char *buffer, + size_t size, + uintptr_t extra) { + C_SIMPLE_HTTP_String_Part *string_part = + malloc(sizeof(C_SIMPLE_HTTP_String_Part)); + + if (buffer && size > 0) { + string_part->size = size; + string_part->buf = malloc(string_part->size - 1); + memcpy(string_part->buf, buffer, string_part->size - 1); + } else { + string_part->size = 0; + string_part->buf = NULL; + } string_part->extra = extra; @@ -264,4 +294,66 @@ void c_simple_http_cleanup_DIR(DIR **fd) { } } +char *c_simple_http_FILE_to_c_str(const char *filename, uint64_t *size_out) { + FILE *fd = fopen(filename, "rb"); + if (!fd) { + fprintf(stderr, "ERROR Failed to open %s!\n", filename); + return NULL; + } else if (fseek(fd, 0, SEEK_END) != 0) { + fprintf(stderr, "ERROR Failed to seek to end of %s!\n", filename); + fclose(fd); + return NULL; + } + + long size = ftell(fd); + if (size < 0) { + fprintf(stderr, "ERROR Failed to get seek pos of end of %s!\n", filename); + fclose(fd); + return NULL; + } else if (size == 0) { + fprintf(stderr, "ERROR Size of file \"%s\" is zero!\n", filename); + fclose(fd); + return NULL; + } + if (size_out) { + *size_out = (uint64_t)size; + } + char *buf = malloc((uint64_t)size + 1); + if (fseek(fd, 0, SEEK_SET) != 0) { + fprintf(stderr, "ERROR Failed to seek to beginning of %s!\n", filename); + free(buf); + fclose(fd); + return NULL; + } else if (fread(buf, 1, (uint64_t)size, fd) != (uint64_t)size) { + fprintf(stderr, "ERROR Failed to read from file %s!\n", filename); + free(buf); + fclose(fd); + return NULL; + } + + buf[size] = 0; + + fclose(fd); + return buf; +} + +size_t c_simple_http_trim_end_whitespace(char *c_str) { + size_t trimmed = 0; + + uint64_t idx= strlen(c_str); + for (; idx-- > 0;) { + if (c_str[idx] == ' ' + || c_str[idx] == '\n' + || c_str[idx] == '\r' + || c_str[idx] == '\t') { + c_str[idx] = 0; + ++trimmed; + } else { + break; + } + } + + return trimmed; +} + // vim: et ts=2 sts=2 sw=2 diff --git a/src/helpers.h b/src/helpers.h index 0039f63..cbe951f 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -56,6 +56,16 @@ void c_simple_http_cleanup_string_part(void *data); void c_simple_http_add_string_part( SDArchiverLinkedList *list, const char *c_string, uintptr_t extra); +/// Puts a malloced instance of String_Part into the list. +/// The given c_string will be copied into a newly malloced buffer. +/// "size" must include NULL if "buffer" is a c_string. +/// If there is no NULL at the end, "size" must be +1 actual size. +void c_simple_http_add_string_part_sized( + SDArchiverLinkedList *list, + const char *buffer, + size_t size, + uintptr_t extra); + /// Combines all String_Parts in the list and returns it as a single buffer. char *c_simple_http_combine_string_parts(const SDArchiverLinkedList *list); @@ -80,6 +90,13 @@ int c_simple_http_helper_mkdir_tree(const char *dirpath); void c_simple_http_cleanup_DIR(DIR **fd); +/// Must be free'd if non-NULL. +char *c_simple_http_FILE_to_c_str(const char *filename, uint64_t *size_out); + +/// Trims by placing NULL bytes in place of whitespace at the end of c_str. +/// Returns number of whitespace trimmed. +size_t c_simple_http_trim_end_whitespace(char *c_str); + #endif // vim: et ts=2 sts=2 sw=2 diff --git a/src/http_template.c b/src/http_template.c index 6d009f8..bf797ae 100644 --- a/src/http_template.c +++ b/src/http_template.c @@ -49,83 +49,479 @@ int c_simple_http_internal_ends_with_FILE(const char *c_string) { return 2; } +int c_simple_http_internal_parse_if_expression( + const C_SIMPLE_HTTP_ParsedConfig *wrapped_hash_map, + const char *var, + const size_t var_offset, + const size_t var_size, + char **left_side_out, + char **right_side_out, + uint_fast8_t *is_equality_out, + SDArchiverLinkedList **files_list_out) { + if (!left_side_out || !right_side_out || !is_equality_out) { + fprintf(stderr, "ERROR Internal error! %s\n", var); + return 1; + } + *left_side_out = NULL; + *right_side_out = NULL; + + __attribute__((cleanup(simple_archiver_list_free))) + SDArchiverLinkedList *var_parts = simple_archiver_list_init(); + char buf[64]; + size_t buf_idx = 0; + size_t idx = var_offset; + ssize_t var_index = -1; + for(; idx < var_size; ++idx) { + if (idx + 1 < var_size && var[idx] == '!' && var[idx + 1] == '=') { + break; + } else if (idx + 1 < var_size && var[idx] == '=' && var[idx + 1] == '=') { + break; + } else if (var[idx] == '[') { + if (var_index >= 0) { + fprintf(stderr, "ERROR \"[\" Encountered twice! %s\n", var); + return 1; + } + idx += 1; + var_index = 0; + for(; idx < var_size; ++idx) { + if (var[idx] == ']') { + break; + } else if (var[idx] >= '0' && var[idx] <= '9') { + var_index = var_index * 10 + ((ssize_t)(var[idx] - '0')); + } else { + fprintf(stderr, "ERROR Syntax error in \"[...]\"! %s\n", var); + return 1; + } + } + if (idx >= var_size) { + fprintf( + stderr, "ERROR End of expression during parsing! %s\n", var); + return 1; + } else if (var[idx] != ']') { + fprintf(stderr, "ERROR No closing \"]\"! %s\n", var); + return 1; + } else if ( + idx + 1 < var_size && var[idx] == '!' && var[idx + 1] == '=') { + break; + } else if ( + idx + 1 < var_size && var[idx] == '=' && var[idx + 1] == '=') { + break; + } else { + fprintf(stderr, "ERROR Invalid expression after \"]\"! %s\n", var); + return 1; + } + } + buf[buf_idx++] = var[idx]; + if (buf_idx >= 63) { + buf[63] = 0; + c_simple_http_add_string_part(var_parts, buf, 0); + buf_idx = 0; + } + } + + if (buf_idx == 0) { + fprintf(stderr, "ERROR Empty VAR in expression! %s\n", var); + return 1; + } else if (idx >= var_size) { + fprintf(stderr, "ERROR Invalid \"IF\" expression! %s\n", var); + return 1; + } + + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *var_buf = NULL; + if (var_parts->count != 0) { + if (buf_idx != 0) { + buf[buf_idx] = 0; + c_simple_http_add_string_part(var_parts, buf, 0); + } + var_buf = c_simple_http_combine_string_parts(var_parts); + } else { + buf[buf_idx] = 0; + var_buf = strdup(buf); + } + buf_idx = 0; + simple_archiver_list_free(&var_parts); + var_parts = simple_archiver_list_init(); + + C_SIMPLE_HTTP_ConfigValue *config_value = + simple_archiver_hash_map_get(wrapped_hash_map->hash_map, + var_buf, + strlen(var_buf) + 1); + if (!config_value || !config_value->value) { + fprintf(stderr, "ERROR Invalid VAR after \"IF/ELSEIF\"! %s\n", var); + return 1; + } else if (var_index >= 0) { + for (size_t var_index_idx = 0; + var_index_idx < var_index; + ++var_index_idx) { + config_value = config_value->next; + if (!config_value) { + fprintf(stderr, "ERROR Array index out of bounds! %s\n", var); + return 1; + } + } + } + + uint64_t left_side_size = 0; + if (c_simple_http_internal_ends_with_FILE(var_buf) == 0) { + *left_side_out = + c_simple_http_FILE_to_c_str(config_value->value, &left_side_size); + if (!*left_side_out || left_side_size == 0) { + fprintf(stderr, "ERROR _FILE variable could not be read! %s\n", var); + if (*left_side_out) { + free(*left_side_out); + *left_side_out = NULL; + } + return 1; + } + c_simple_http_trim_end_whitespace(*left_side_out); + left_side_size = strlen(*left_side_out); + if (files_list_out) { + simple_archiver_list_add(*files_list_out, + strdup(config_value->value), + NULL); + } + } else { + *left_side_out = strdup(config_value->value); + c_simple_http_trim_end_whitespace(*left_side_out); + left_side_size = strlen(*left_side_out); + } + + if (idx + 1 < var_size && var[idx] == '=' && var[idx + 1] == '=') { + *is_equality_out = 1; + } else if (idx + 1 < var_size && var[idx] == '!' && var[idx] == '=') { + *is_equality_out = 0; + } else { + fprintf(stderr, "ERROR Operation not n/eq! %s\n", var); + return 1; + } + + idx += 2; + + for (; idx < var_size; ++idx) { + buf[buf_idx++] = var[idx]; + if (buf_idx >= 63) { + buf[63] = 0; + c_simple_http_add_string_part(var_parts, buf, 0); + buf_idx = 0; + } + } + + if (var_parts->count != 0) { + if (buf_idx != 0) { + buf[buf_idx] = 0; + c_simple_http_add_string_part(var_parts, buf, 0); + } + *right_side_out = c_simple_http_combine_string_parts(var_parts); + } else { + buf[buf_idx] = 0; + *right_side_out = strdup(buf); + } + c_simple_http_trim_end_whitespace(*right_side_out); + uint64_t right_side_size = strlen(*right_side_out); + if (right_side_size == 0) { + fprintf(stderr, "ERROR Right side is empty! %s\n", var); + return 1; + } + simple_archiver_list_free(&var_parts); + var_parts = simple_archiver_list_init(); + buf_idx = 0; + return 0; +} + /// Returns zero on success. int c_simple_http_internal_handle_inside_delimeters( - const size_t idx, + uint32_t *state, + const size_t html_buf_idx, const char *var, const size_t var_size, const C_SIMPLE_HTTP_ParsedConfig *wrapped_hash_map, SDArchiverLinkedList *string_part_list, SDArchiverLinkedList **files_list_out) { C_SIMPLE_HTTP_String_Part string_part; - C_SIMPLE_HTTP_ConfigValue *config_value = - simple_archiver_hash_map_get( - wrapped_hash_map->hash_map, - var, - (uint32_t)var_size + 1); - if (config_value && config_value->value) { - if (c_simple_http_internal_ends_with_FILE(var) == 0) { - // Load from file specified by "config_value->value". - __attribute__((cleanup(simple_archiver_helper_cleanup_FILE))) - FILE *f = fopen(config_value->value, "r"); - if (!f) { - fprintf(stderr, "ERROR Failed to open file \"%s\"!\n", - config_value->value); + if (var_size == 0) { + fprintf(stderr, "ERROR No characters within delimeters!\n"); + return 1; + } else if (var[0] == '!') { + // Is an expression. + if (strncmp(var + 1, "IF ", 3) == 0) { + if (((*state) & 0xE) != 0) { + fprintf(stderr, "ERROR Invalid \"IF\" (bad state)! %s\n", var); return 1; - } else if (fseek(f, 0, SEEK_END) != 0) { - fprintf(stderr, "ERROR Failed to seek to end of file \"%s\"!\n", - config_value->value); + } + + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *left_side = NULL; + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *right_side = NULL; + + uint_fast8_t is_equality = 0; + + if (c_simple_http_internal_parse_if_expression( + wrapped_hash_map, + var, + 1 + 3, + var_size, + &left_side, + &right_side, + &is_equality, + files_list_out)) { + return 1; + } else if (!left_side || !right_side) { + fprintf(stderr, "ERROR Internal error! %s\n", var); + return 1; + } + + if (is_equality) { + if (strcmp(left_side, right_side) == 0) { + // Is equality is TRUE. + (*state) |= 0x12; + } else { + // Is equality is FALSE. + (*state) |= 4; + } + } else { + if (strcmp(left_side, right_side) == 0) { + // Is inequality is FALSE. + (*state) |= 4; + } else { + // Is inequality is TRUE. + (*state) |= 0x12; + } + } + c_simple_http_add_string_part(string_part_list, NULL, html_buf_idx + 1); + } else if (strncmp(var + 1, "ELSEIF ", 7) == 0) { + if (((*state) & 0xE) != 2 && ((*state) & 0xE) != 4) { + fprintf(stderr, "ERROR Invalid \"ELSEIF\" (bad state)! %s\n", var); return 1; } - long file_size = ftell(f); - if (file_size <= 0) { - fprintf(stderr, "ERROR Size of file \"%s\" is invalid!\n", - config_value->value); + + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *left_side = NULL; + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *right_side = NULL; + + uint_fast8_t is_equality = 0; + + if (c_simple_http_internal_parse_if_expression( + wrapped_hash_map, + var, + 1 + 7, + var_size, + &left_side, + &right_side, + &is_equality, + files_list_out)) { return 1; - } else if (fseek(f, 0, SEEK_SET) != 0) { - fprintf(stderr, "ERROR Failed to seek to start of file " - "\"%s\"!\n", - config_value->value); + } else if (!left_side || !right_side) { + fprintf(stderr, "ERROR Internal error! %s\n", var); return 1; } - string_part.size = (size_t)file_size + 1; - string_part.buf = malloc(string_part.size); - string_part.extra = idx + 1; - if (fread(string_part.buf, - string_part.size - 1, - 1, - f) - != 1) { - fprintf(stderr, "ERROR Failed to read from file \"%s\"!\n", - config_value->value); + if (((*state) & 0x10) == 0) { + if (is_equality) { + if (strcmp(left_side, right_side) == 0) { + // Is equality is TRUE. + (*state) &= 0xFFFFFFE1; + (*state) |= 0x16; + } else { + // Is equality is FALSE. + (*state) &= 0xFFFFFFE1; + (*state) |= 8; + } + } else { + if (strcmp(left_side, right_side) == 0) { + // Is inequality is FALSE. + (*state) &= 0xFFFFFFE1; + (*state) |= 8; + } else { + // Is inequality is TRUE. + (*state) &= 0xFFFFFFE1; + (*state) |= 0x16; + } + } + } else { + // Previous IF/ELSEIF resolved true. + (*state) &= 0xFFFFFFF1; + (*state) |= 8; + } + c_simple_http_add_string_part(string_part_list, NULL, html_buf_idx + 1); + } else if (strncmp(var + 1, "ELSE", 4) == 0) { + if (((*state) & 0x10) == 0) { + // No previous expression resolved to true, enabling ELSE block. + (*state) &= 0xFFFFFFE1; + (*state) |= 0xA; + } else { + // No expression resolved to true, disabling ELSE block. + (*state) &= 0xFFFFFFE1; + (*state) |= 0xC; + } + c_simple_http_add_string_part(string_part_list, NULL, html_buf_idx + 1); + } else if (strncmp(var + 1, "ENDIF", 5) == 0) { + if (((*state) & 0xE) == 0) { + fprintf( + stderr, + "ERROR Invalid expression (no preceeding if/else)! %s\n", + var); + return 1; + } + (*state) &= 0xFFFFFFE1; + c_simple_http_add_string_part(string_part_list, NULL, html_buf_idx + 1); + } else if (strncmp(var + 1, "INDEX ", 6) == 0) { + // Indexing into variable array. + + __attribute__((cleanup(simple_archiver_list_free))) + SDArchiverLinkedList *var_parts = simple_archiver_list_init(); + char buf[64]; + size_t buf_idx = 0; + + size_t idx = 7; + for (; idx < var_size; ++idx) { + if (var[idx] != '[') { + buf[buf_idx++] = var[idx]; + if (buf_idx >= 63) { + buf[63] = 0; + c_simple_http_add_string_part(var_parts, buf, 0); + buf_idx = 0; + } + } else { + break; + } + } + if (var[idx] != '[' || idx >= var_size) { + fprintf(stderr, "ERROR Syntax error! %s\n", var); return 1; } - string_part.buf[string_part.size - 1] = 0; - if (files_list_out) { - char *variable_filename = malloc(strlen(config_value->value) + 1); - strcpy(variable_filename, config_value->value); - simple_archiver_list_add( - *files_list_out, variable_filename, NULL); + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *var_name = NULL; + if (var_parts->count != 0) { + if (buf_idx != 0) { + buf[buf_idx] = 0; + c_simple_http_add_string_part(var_parts, buf, 0); + buf_idx = 0; + } + var_name = c_simple_http_combine_string_parts(var_parts); + } else if (buf_idx != 0) { + buf[buf_idx] = 0; + var_name = strdup(buf); + } else { + fprintf(stderr, "ERROR Empty variable name string! %s\n", var); + return 1; } + C_SIMPLE_HTTP_ConfigValue *config_value = + simple_archiver_hash_map_get(wrapped_hash_map->hash_map, + var_name, + strlen(var_name) + 1); + if (!config_value) { + fprintf(stderr, "ERROR Variable not found in config! %s\n", var); + return 1; + } + + idx += 1; + ssize_t array_index = 0; + for (; idx < var_size; ++idx) { + if (var[idx] >= '0' && var[idx] <= '9') { + array_index = array_index * 10 + (ssize_t)(var[idx] - '0'); + } else if (var[idx] == ']') { + break; + } else { + fprintf(stderr, "ERROR Syntax error getting index! %s\n", var); + return 1; + } + } + if (idx >= var_size) { + fprintf(stderr, "ERROR Syntax error getting index (reached end without " + "reaching \"]\")! %s\n", var); + return 1; + } + for (; array_index-- > 0;) { + config_value = config_value->next; + if (!config_value) { + fprintf(stderr, "ERROR Array index out of bounds! %s\n", var); + return 1; + } + } + if (!config_value || !config_value->value) { + fprintf( + stderr, "ERROR Internal error invalid indexed value! %s\n", var); + return 1; + } + + __attribute__((cleanup(simple_archiver_helper_cleanup_c_string))) + char *value_contents = NULL; + uint64_t value_contents_size = 0; + if (c_simple_http_internal_ends_with_FILE(var_name) == 0) { + value_contents = c_simple_http_FILE_to_c_str( + config_value->value, &value_contents_size); + if (!value_contents || value_contents_size == 0) { + fprintf(stderr, "ERROR Failed to get value from FILE! %s\n", var); + return 1; + } + if (files_list_out) { + simple_archiver_list_add(*files_list_out, + strdup(config_value->value), + NULL); + } + } else { + value_contents = strdup(config_value->value); + value_contents_size = strlen(value_contents); + } + + c_simple_http_add_string_part_sized(string_part_list, + value_contents, + value_contents_size + 1, + html_buf_idx + 1); } else { - // Variable data is "config_value->value". - string_part.size = strlen(config_value->value) + 1; - string_part.buf = malloc(string_part.size); - memcpy(string_part.buf, config_value->value, string_part.size); - string_part.buf[string_part.size - 1] = 0; - string_part.extra = idx + 1; + fprintf(stderr, "ERROR Invalid expression! %s\n", var); + return 1; } } else { - string_part.buf = NULL; - string_part.size = 0; - string_part.extra = idx + 1; - } - c_simple_http_add_string_part(string_part_list, - string_part.buf, - string_part.extra); - if (string_part.buf) { - free(string_part.buf); + // Refers to a variable by name. + C_SIMPLE_HTTP_ConfigValue *config_value = + simple_archiver_hash_map_get( + wrapped_hash_map->hash_map, + var, + (uint32_t)var_size + 1); + if (config_value && config_value->value) { + if (c_simple_http_internal_ends_with_FILE(var) == 0) { + // Load from file specified by "config_value->value". + uint64_t size = 0; + string_part.buf = c_simple_http_FILE_to_c_str(config_value->value, &size); + if (!string_part.buf || size == 0) { + fprintf(stderr, "ERROR Failed to read from file \"%s\"!\n", + config_value->value); + if (string_part.buf) { + free(string_part.buf); + } + return 1; + } + string_part.size = size + 1; + string_part.extra = html_buf_idx + 1; + + string_part.buf[string_part.size - 1] = 0; + if (files_list_out) { + char *variable_filename = strdup(config_value->value); + simple_archiver_list_add(*files_list_out, variable_filename, NULL); + } + } else { + // Variable data is "config_value->value". + string_part.size = strlen(config_value->value) + 1; + string_part.buf = malloc(string_part.size); + memcpy(string_part.buf, config_value->value, string_part.size); + string_part.buf[string_part.size - 1] = 0; + string_part.extra = html_buf_idx + 1; + } + } else { + string_part.buf = NULL; + string_part.size = 0; + string_part.extra = html_buf_idx + 1; + } + c_simple_http_add_string_part(string_part_list, + string_part.buf, + string_part.extra); + if (string_part.buf) { + free(string_part.buf); + } } return 0; @@ -211,6 +607,13 @@ char *c_simple_http_path_to_generated( // xxxx xxx0 - Initial state, no delimeter reached. // xxxx xxx1 - Three left-curly-brace delimeters reached. + // xxxx 001x - If expression ALLOW contents. + // xxxx 010x - If expression DISALLOW contents. + // xxxx 011x - ElseIf expression ALLOW contents. + // xxxx 100x - ElseIf expression DISALLOW contents. + // xxxx 101x - Else expression ALLOW contents. + // xxxx 110x - Else expression DISALLOW contents. + // xxx1 xxxx - Previous If/ElseIf had true/ALLOW. uint32_t state = 0; for (; idx < html_buf_size; ++idx) { @@ -221,23 +624,30 @@ char *c_simple_http_path_to_generated( if (delimeter_count >= 3) { delimeter_count = 0; state |= 1; - if (string_part_list->count != 0) { - C_SIMPLE_HTTP_String_Part *last_part = - string_part_list->tail->prev->data; - last_template_idx = last_part->extra; + if ((state & 0xE) == 0 + || (state & 0xE) == 2 + || (state & 0xE) == 6 + || (state & 0xE) == 0xA) { + if (string_part_list->count != 0) { + C_SIMPLE_HTTP_String_Part *last_part = + string_part_list->tail->prev->data; + last_template_idx = last_part->extra; + } + string_part.size = idx - last_template_idx - 1; + string_part.buf = malloc(string_part.size); + memcpy( + string_part.buf, + html_buf + last_template_idx, + string_part.size); + string_part.buf[string_part.size - 1] = 0; + string_part.extra = idx + 1; + c_simple_http_add_string_part(string_part_list, + string_part.buf, + string_part.extra); + free(string_part.buf); + } else { + c_simple_http_add_string_part(string_part_list, NULL, idx + 1); } - string_part.size = idx - last_template_idx - 1; - string_part.buf = malloc(string_part.size); - memcpy( - string_part.buf, - html_buf + last_template_idx, - string_part.size); - string_part.buf[string_part.size - 1] = 0; - string_part.extra = idx + 1; - c_simple_http_add_string_part(string_part_list, - string_part.buf, - string_part.extra); - free(string_part.buf); } } else { delimeter_count = 0; @@ -261,6 +671,7 @@ char *c_simple_http_path_to_generated( var_size); var[var_size] = 0; if (c_simple_http_internal_handle_inside_delimeters( + &state, idx, var, var_size, diff --git a/src/test.c b/src/test.c index 0699574..9034a16 100644 --- a/src/test.c +++ b/src/test.c @@ -620,6 +620,46 @@ int main(int argc, char **argv) { CHECK_TRUE(ret == 0); CHECK_TRUE(ret2 == 0); CHECK_TRUE(ret3 == 0); + + buf = strdup("EndingWithThreeTabs\t\t\t"); + CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3); + CHECK_TRUE(buf[18] == 's'); + CHECK_TRUE(buf[19] == 0); + CHECK_TRUE(buf[20] == 0); + CHECK_TRUE(buf[21] == 0); + CHECK_TRUE(buf[22] == 0); + free(buf); + buf = NULL; + + buf = strdup("EndingWithThreeSpaces "); + CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3); + CHECK_TRUE(buf[20] == 's'); + CHECK_TRUE(buf[21] == 0); + CHECK_TRUE(buf[22] == 0); + CHECK_TRUE(buf[23] == 0); + CHECK_TRUE(buf[24] == 0); + free(buf); + buf = NULL; + + buf = strdup("EndingWithThreeNewlines\n\n\n"); + CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3); + CHECK_TRUE(buf[22] == 's'); + CHECK_TRUE(buf[23] == 0); + CHECK_TRUE(buf[24] == 0); + CHECK_TRUE(buf[25] == 0); + CHECK_TRUE(buf[26] == 0); + free(buf); + buf = NULL; + + buf = strdup("EndingWithThreeCarraigeReturns\r\r\r"); + CHECK_TRUE(c_simple_http_trim_end_whitespace(buf) == 3); + CHECK_TRUE(buf[29] == 's'); + CHECK_TRUE(buf[30] == 0); + CHECK_TRUE(buf[31] == 0); + CHECK_TRUE(buf[32] == 0); + CHECK_TRUE(buf[33] == 0); + free(buf); + buf = NULL; } // Test html_cache. -- 2.49.0