#include "patch.h"
#include "logging.h"
#include <string.h>
#include <stdlib.h>
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;
}