Skip to content

Instantly share code, notes, and snippets.

@carlkl
Created February 25, 2026 14:24
Show Gist options
  • Select an option

  • Save carlkl/e430501c6b029a7acc85c087e2e3dfa9 to your computer and use it in GitHub Desktop.

Select an option

Save carlkl/e430501c6b029a7acc85c087e2e3dfa9 to your computer and use it in GitHub Desktop.
binary patch msys2 executable for https://github.com/msys2/MINGW-packages/issues/27782
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <stdint.h>
#include <errno.h>
#include <limits.h>
/* For large file support on 32-bit systems */
#ifdef _WIN32
#define fseeko _fseeki64
#define ftello _ftelli64
#endif
#define DEFAULT_MAX_SIZE_MB 10 /* 10 MB default */
#define MAX_ALLOWED_SIZE_MB 4096 /* 4 GB absolute maximum to prevent overflow */
#define SEARCH_STR "D:/M/"
#define REPLACE_STR "C:/M/"
#define PATTERN_LEN 5 /* Both strings are 5 chars */
/* Safe MB to bytes conversion with overflow check */
static int mb_to_bytes_safe(size_t mb, size_t *bytes_out) {
if (mb > MAX_ALLOWED_SIZE_MB) {
return -1; /* Would overflow or exceed limit */
}
/* Safe calculation: mb * 1024 * 1024 */
if (mb > SIZE_MAX / (1024 * 1024)) {
return -1; /* Would overflow size_t */
}
*bytes_out = mb * 1024 * 1024;
return 0;
}
/* Print usage information */
void print_usage(const char *prog_name) {
fprintf(stderr, "Usage: %s [OPTIONS] <input_file>\n", prog_name);
fprintf(stderr, "\nOptions:\n");
fprintf(stderr, " -s, --size <MB> Maximum file size in megabytes (default: %d MB, max: %d MB)\n",
DEFAULT_MAX_SIZE_MB, MAX_ALLOWED_SIZE_MB);
fprintf(stderr, " -f, --force Overwrite output file if it exists\n");
fprintf(stderr, " -h, --help Show this help message\n");
fprintf(stderr, "\nDescription:\n");
fprintf(stderr, " Reads a binary file, replaces all occurrences of '%s' with '%s',\n", SEARCH_STR, REPLACE_STR);
fprintf(stderr, " and saves the result to a new file with '_modified' suffix.\n");
}
/* Safe string to unsigned long conversion with validation */
static int parse_size_mb(const char *str, size_t *result) {
char *endptr;
unsigned long val;
if (str == NULL || *str == '\0') {
return -1;
}
/* Check for negative sign (invalid for size) */
if (*str == '-') {
return -1;
}
errno = 0;
val = strtoul(str, &endptr, 10);
/* Check for conversion errors */
if (errno == ERANGE || val > SIZE_MAX) {
return -1; /* Overflow */
}
if (endptr == str) {
return -1; /* No digits found */
}
if (*endptr != '\0') {
return -1; /* Trailing garbage */
}
if (val == 0) {
return -1; /* Zero is not valid */
}
if (val > MAX_ALLOWED_SIZE_MB) {
return -1; /* Exceeds maximum */
}
*result = (size_t)val;
return 0;
}
/* Check if file exists */
static int file_exists(const char *filename) {
FILE *f = fopen(filename, "rb");
if (f != NULL) {
fclose(f);
return 1;
}
return 0;
}
/* Generate output filename by adding '_modified' before extension */
char *generate_output_filename(const char *input_name) {
char *output_name;
const char *dot_pos;
const char *slash_pos;
size_t base_len;
size_t total_len;
const char *suffix = "_modified";
size_t suffix_len = strlen(suffix);
size_t input_len = strlen(input_name);
/* Sanity check on input length */
if (input_len == 0 || input_len > 4096) {
return NULL;
}
/* Find the last dot for extension, but only after last path separator */
slash_pos = strrchr(input_name, '/');
if (slash_pos == NULL) {
slash_pos = strrchr(input_name, '\\'); /* Windows path separator */
}
dot_pos = strrchr(input_name, '.');
/* Ensure dot is in filename part, not in directory path */
if (dot_pos != NULL && slash_pos != NULL && dot_pos < slash_pos) {
dot_pos = NULL; /* Dot is in directory path, ignore it */
}
/* Check for overflow in allocation */
total_len = input_len + suffix_len + 1;
if (total_len < input_len) { /* Overflow check */
return NULL;
}
output_name = malloc(total_len);
if (output_name == NULL) {
return NULL;
}
if (dot_pos != NULL && dot_pos > input_name) {
base_len = (size_t)(dot_pos - input_name);
/* Copy base name */
memcpy(output_name, input_name, base_len);
output_name[base_len] = '\0';
/* Add suffix */
strcat(output_name, suffix);
/* Add extension */
strcat(output_name, dot_pos);
} else {
/* No extension found */
strcpy(output_name, input_name);
strcat(output_name, suffix);
}
return output_name;
}
/* Replace all occurrences of search pattern with replace pattern in buffer */
size_t replace_pattern(uint8_t *buffer, size_t size) {
size_t count = 0;
size_t i;
if (buffer == NULL || size < PATTERN_LEN) {
return 0;
}
for (i = 0; i <= size - PATTERN_LEN; i++) {
if (memcmp(&buffer[i], SEARCH_STR, PATTERN_LEN) == 0) {
memcpy(&buffer[i], REPLACE_STR, PATTERN_LEN);
count++;
i += PATTERN_LEN - 1; /* Skip past the replacement */
}
}
return count;
}
/* Get file size safely */
static int get_file_size(FILE *fp, size_t *size_out) {
long long file_size;
#ifdef _WIN32
if (_fseeki64(fp, 0, SEEK_END) != 0) {
return -1;
}
file_size = _ftelli64(fp);
#else
if (fseeko(fp, 0, SEEK_END) != 0) {
return -1;
}
file_size = ftello(fp);
#endif
if (file_size < 0) {
return -1; /* ftell/ftello error */
}
if ((unsigned long long)file_size > SIZE_MAX) {
return -1; /* File too large for size_t */
}
#ifdef _WIN32
if (_fseeki64(fp, 0, SEEK_SET) != 0) {
return -1;
}
#else
if (fseeko(fp, 0, SEEK_SET) != 0) {
return -1;
}
#endif
*size_out = (size_t)file_size;
return 0;
}
int main(int argc, char *argv[]) {
int opt;
size_t max_size_mb = DEFAULT_MAX_SIZE_MB;
size_t max_size_bytes;
const char *input_filename = NULL;
char *output_filename = NULL;
FILE *input_file = NULL;
FILE *output_file = NULL;
uint8_t *buffer = NULL;
size_t file_size = 0;
size_t bytes_read;
size_t replacements;
int ret = EXIT_FAILURE;
int force_overwrite = 0;
/* Long options for getopt */
static struct option long_options[] = {
{"size", required_argument, 0, 's'},
{"force", no_argument, 0, 'f'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
/* Parse command line arguments */
while ((opt = getopt_long(argc, argv, "s:fh", long_options, NULL)) != -1) {
switch (opt) {
case 's':
if (parse_size_mb(optarg, &max_size_mb) != 0) {
fprintf(stderr, "Error: Invalid size '%s'. Must be a positive integer (1-%d).\n",
optarg, MAX_ALLOWED_SIZE_MB);
return EXIT_FAILURE;
}
break;
case 'f':
force_overwrite = 1;
break;
case 'h':
print_usage(argv[0]);
return EXIT_SUCCESS;
default:
print_usage(argv[0]);
return EXIT_FAILURE;
}
}
/* Check for input filename */
if (optind >= argc) {
fprintf(stderr, "Error: No input file specified.\n");
print_usage(argv[0]);
return EXIT_FAILURE;
}
input_filename = argv[optind];
/* Validate input filename */
if (strlen(input_filename) == 0) {
fprintf(stderr, "Error: Empty filename provided.\n");
return EXIT_FAILURE;
}
/* Convert MB to bytes with overflow protection */
if (mb_to_bytes_safe(max_size_mb, &max_size_bytes) != 0) {
fprintf(stderr, "Error: Size value causes overflow.\n");
return EXIT_FAILURE;
}
/* Open input file in binary mode */
input_file = fopen(input_filename, "rb");
if (input_file == NULL) {
perror("Error opening input file");
return EXIT_FAILURE;
}
/* Get file size safely */
if (get_file_size(input_file, &file_size) != 0) {
fprintf(stderr, "Error: Failed to determine file size.\n");
goto cleanup;
}
printf("Input file: %s\n", input_filename);
printf("File size: %zu bytes (%.2f MB)\n", file_size, (double)file_size / (1024 * 1024));
printf("Maximum allowed size: %zu MB (%zu bytes)\n", max_size_mb, max_size_bytes);
/* Check for empty file */
if (file_size == 0) {
fprintf(stderr, "Error: Input file is empty.\n");
goto cleanup;
}
/* Check file size against maximum */
if (file_size > max_size_bytes) {
fprintf(stderr, "Error: File size (%.2f MB) exceeds maximum allowed size (%zu MB).\n",
(double)file_size / (1024 * 1024), max_size_mb);
goto cleanup;
}
/* Generate output filename before allocation (fail fast) */
output_filename = generate_output_filename(input_filename);
if (output_filename == NULL) {
fprintf(stderr, "Error: Failed to generate output filename.\n");
goto cleanup;
}
/* Check if output file already exists */
if (!force_overwrite && file_exists(output_filename)) {
fprintf(stderr, "Error: Output file '%s' already exists. Use -f to overwrite.\n",
output_filename);
goto cleanup;
}
printf("Output file: %s\n", output_filename);
/* Allocate buffer */
buffer = (uint8_t *)malloc(file_size);
if (buffer == NULL) {
fprintf(stderr, "Error: Failed to allocate memory for file buffer (%zu bytes).\n", file_size);
goto cleanup;
}
/* Read entire file into buffer */
bytes_read = fread(buffer, 1, file_size, input_file);
if (bytes_read != file_size) {
fprintf(stderr, "Error: Failed to read complete file (read %zu of %zu bytes).\n",
bytes_read, file_size);
if (ferror(input_file)) {
perror("Read error");
}
goto cleanup;
}
fclose(input_file);
input_file = NULL;
/* Perform replacement */
replacements = replace_pattern(buffer, file_size);
printf("Replacements made: %zu occurrences of '%s' -> '%s'\n",
replacements, SEARCH_STR, REPLACE_STR);
/* Open output file in binary mode */
output_file = fopen(output_filename, "wb");
if (output_file == NULL) {
perror("Error opening output file");
goto cleanup;
}
/* Write buffer to output file */
if (fwrite(buffer, 1, file_size, output_file) != file_size) {
fprintf(stderr, "Error: Failed to write complete file.\n");
if (ferror(output_file)) {
perror("Write error");
}
goto cleanup;
}
/* Ensure data is flushed to disk */
if (fflush(output_file) != 0) {
perror("Error flushing output file");
goto cleanup;
}
printf("Successfully wrote %zu bytes to output file.\n", file_size);
ret = EXIT_SUCCESS;
cleanup:
if (input_file != NULL) {
fclose(input_file);
}
if (output_file != NULL) {
fclose(output_file);
/* If we failed, try to remove partial output file */
if (ret != EXIT_SUCCESS && output_filename != NULL) {
remove(output_filename);
}
}
if (buffer != NULL) {
/* Clear sensitive data before freeing (defense in depth) */
memset(buffer, 0, file_size);
free(buffer);
}
if (output_filename != NULL) {
free(output_filename);
}
return ret;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment