|
#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;
|
|
}
|