#include "patch.h" #include "logging.h" #include #include int patch_has_rules(const patch_config_t *config) { return config && config->rule_count > 0; } int patch_check_for_block(const patch_config_t *config, const char *data, size_t len) { if (!config || !data || len == 0) return 0; for (int i = 0; i < config->rule_count; i++) { const patch_rule_t *rule = &config->rules[i]; if (!rule->is_null) continue; if (rule->key_len == 0 || rule->key_len > len) continue; if (memmem(data, len, rule->key, rule->key_len) != NULL) { return 1; } } return 0; } patch_result_t patch_apply( const patch_config_t *config, const char *input, size_t input_len, char *output, size_t output_capacity ) { patch_result_t result = {0, 0, 0}; if (!config || config->rule_count == 0 || !input || input_len == 0) { if (output && output_capacity >= input_len) { memcpy(output, input, input_len); result.output_len = input_len; } return result; } if (patch_check_for_block(config, input, input_len)) { result.should_block = 1; return result; } int has_replacements = 0; for (int i = 0; i < config->rule_count; i++) { if (!config->rules[i].is_null && config->rules[i].key_len > 0) { has_replacements = 1; break; } } if (!has_replacements) { if (output && output_capacity >= input_len) { memcpy(output, input, input_len); result.output_len = input_len; } return result; } size_t max_growth = 0; for (int i = 0; i < config->rule_count; i++) { const patch_rule_t *rule = &config->rules[i]; if (rule->is_null || rule->key_len == 0) continue; if (rule->value_len > rule->key_len) { size_t growth_per_match = rule->value_len - rule->key_len; size_t max_matches = input_len / rule->key_len + 1; max_growth += growth_per_match * max_matches; } } size_t work_size = input_len + max_growth + 1; char *work_buf = malloc(work_size); if (!work_buf) { if (output && output_capacity >= input_len) { memcpy(output, input, input_len); result.output_len = input_len; } return result; } memcpy(work_buf, input, input_len); size_t work_len = input_len; for (int i = 0; i < config->rule_count; i++) { const patch_rule_t *rule = &config->rules[i]; if (rule->is_null || rule->key_len == 0) continue; size_t pos = 0; while (pos + rule->key_len <= work_len) { char *found = memmem(work_buf + pos, work_len - pos, rule->key, rule->key_len); if (!found) break; size_t match_pos = found - work_buf; size_t tail_len = work_len - match_pos - rule->key_len; if (rule->value_len != rule->key_len) { size_t new_work_len = work_len - rule->key_len + rule->value_len; if (new_work_len >= work_size) { size_t new_size = new_work_len + max_growth + 1; char *new_buf = realloc(work_buf, new_size); if (!new_buf) { pos = match_pos + rule->key_len; continue; } work_buf = new_buf; work_size = new_size; found = work_buf + match_pos; } memmove(found + rule->value_len, found + rule->key_len, tail_len); work_len = new_work_len; } if (rule->value_len > 0) { memcpy(found, rule->value, rule->value_len); } pos = match_pos + rule->value_len; } } result.size_delta = (long)work_len - (long)input_len; if (output && output_capacity >= work_len) { memcpy(output, work_buf, work_len); result.output_len = work_len; } else if (output && output_capacity > 0) { memcpy(output, work_buf, output_capacity); result.output_len = output_capacity; } free(work_buf); return result; }