Created
February 25, 2026 14:24
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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