Replace string

This simple C program replaces one string with another in standard input or in one or more files.

C code

/* rp (replace string)

Replace one string with another in standard input or in one or more files.

Original version: 2018-04-27

Version: 1.9

Date: 2021-11-28 */


/* Standard header file for islower, toupper */

#include <ctype.h>

/* Standard header file for bool, false, and true */

#include <stdbool.h>

/* Standard header file for NULL */

#include <stddef.h>

/* Standard header file for fclose, feof, fgetc, fopen, fprintf, fputc, fputs, fwrite, perror, printf, puts */

#include <stdio.h>

/* Standard header file for EXIT_FAILURE, EXIT_SUCCESS, free, malloc */

#include <stdlib.h>

/* Standard header file for strcmp, strlen */

#include <string.h>


/* Returns the position of a single-byte character in a string */

unsigned char position_of_character_in_string (char const * const string, char const character)

{

unsigned char position = 0;

char current_character = string [position];

while ((current_character != character) && (current_character != 0))

{

position ++;

current_character = string [position];

}

return position;

}


/* Converts a character to a control character. */

char control_character (char character)

{

char control_byte;

if (islower (character))

{

control_byte = toupper (character);

}

else

{

control_byte = character;

}

control_byte ^= (1 << 6);

return control_byte;

}


/* Gives the value of a digit. */

char digit_value (char const digit)

{

char value;

if (digit == '0')

{

value = 0;

}

else if (digit == '1')

{

value = 1;

}

else if (digit == '2')

{

value = 2;

}

else if (digit == '3')

{

value = 3;

}

else if (digit == '4')

{

value = 4;

}

else if (digit == '5')

{

value = 5;

}

else if (digit == '6')

{

value = 6;

}

else if (digit == '7')

{

value = 7;

}

else if (digit == '8')

{

value = 8;

}

else if (digit == '9')

{

value = 9;

}

else if ((digit == 'A') || (digit == 'a'))

{

value = 10;

}

else if ((digit == 'B') || (digit == 'b'))

{

value = 11;

}

else if ((digit == 'C') || (digit == 'c'))

{

value = 12;

}

else if ((digit == 'D') || (digit == 'd'))

{

value = 13;

}

else if ((digit == 'E') || (digit == 'e'))

{

value = 14;

}

else if ((digit == 'F') || (digit == 'f'))

{

value = 15;

}

else

{

value = 127;

}

return value;

}


/* Writes a string to a buffer */

unsigned long int write_string_to_buffer (char const * const string, unsigned char * const buffer)

{

unsigned long int buffer_position = 0;

unsigned long int string_position = 0;

char character = string [string_position];

bool control = false;

unsigned long int control_position = 0;

unsigned char base = 0;

unsigned char digit_count = 0;

bool escape = false;

while (character != 0)

{

if (base != 0)

{

char value = digit_value (character);

if (value < base)

{

if (digit_count == 0)

{

buffer_position --;

buffer [buffer_position - 1] = value;

}

else if (base == 8)

{

buffer [buffer_position - 1] <<= 3;

buffer [buffer_position - 1] |= value;

}

else if (base == 16)

{

buffer [buffer_position - 1] <<= 4;

buffer [buffer_position - 1] |= value;

}

else

{

buffer [buffer_position - 1] *= base;

buffer [buffer_position - 1] += value;

}

digit_count ++;

}

else

{

base = 0;

digit_count = 0;

}

}

if ((((base == 8) || (base == 10)) && (digit_count == 3)) || ((base == 16) && (digit_count == 2)))

{

base = 0;

digit_count = 0;

}

else if (base ==0)

{

if (escape == false)

{

buffer [buffer_position] = character;

buffer_position ++;

if (character == '\\')

{

escape = true;

}

}

else

{

escape = false;

if (character == '0')

{

buffer [buffer_position - 1] = 0;

base = 8;

digit_count = 1;

}

else if (character == '1')

{

buffer [buffer_position - 1] = 1;

base = 8;

digit_count = 1;

}

else if (character == '2')

{

buffer [buffer_position - 1] = 2;

base = 8;

digit_count = 1;

}

else if (character == '3')

{

buffer [buffer_position - 1] = 3;

base = 8;

digit_count = 1;

}

else if (character == '4')

{

buffer [buffer_position - 1] = 4;

base = 8;

digit_count = 1;

}

else if (character == '5')

{

buffer [buffer_position - 1] = 5;

base = 8;

digit_count = 1;

}

else if (character == '6')

{

buffer [buffer_position - 1] = 6;

base = 8;

digit_count = 1;

}

else if (character == '7')

{

buffer [buffer_position - 1] = 7;

base = 8;

digit_count = 1;

}

else if (character == 'a')

{

buffer [buffer_position - 1] = '\a';

}

else if (character == 'b')

{

buffer [buffer_position - 1] = '\b';

}

else if (character == 'c')

{

if (string [string_position + 1] != 0)

{

if (control == true)

{

buffer [control_position] = control_character (buffer [control_position]);

}

else

{

control = true;

}

buffer_position --;

control_position = buffer_position;

}

else

{

buffer [buffer_position] = character;

buffer_position ++;

}

}

else if (character == 'd')

{

buffer [buffer_position] = character;

buffer_position ++;

base = 10;

}

else if (character == 'f')

{

buffer [buffer_position - 1] = '\f';

}

else if (character == 'n')

{

buffer [buffer_position - 1] = '\n';

}

else if (character == 'o')

{

buffer [buffer_position] = character;

buffer_position ++;

base = 8;

}

else if (character == 'r')

{

buffer [buffer_position - 1] = '\r';

}

else if (character == 't')

{

buffer [buffer_position - 1] = '\t';

}

else if (character == 'v')

{

buffer [buffer_position - 1] = '\v';

}

else if (character == 'x')

{

buffer [buffer_position] = character;

buffer_position ++;

base = 16;

}

else if (character != '\\')

{

/* Not a valid escape character, so include the escaped characters in the buffer. */

buffer [buffer_position] = character;

buffer_position ++;

}

}

}

string_position ++;

character = string [string_position];

}

if (control == true)

{

buffer [control_position] = control_character (buffer [control_position]);

control = false;

}

return buffer_position;

}


/* Replace the find buffer with the replace buffer in the input file. */

int replace_string_in_file (char const * const program_file_name, char const * const input_file_name, unsigned char const * const find_buffer, unsigned long int const find_buffer_size, unsigned char const * const replace_buffer, unsigned long int const replace_buffer_size)

{

int return_value = EXIT_SUCCESS;

FILE * input_file;

if (input_file_name == NULL)

{

input_file = stdin;

}

else

{

input_file = fopen (input_file_name, "rb");

}

if (input_file == NULL)

{

fprintf (stderr, "%s:", program_file_name);

perror (input_file_name);

return_value = EXIT_FAILURE;

}

else

{

/* Find string */

unsigned long int matched_characters = 0;

do

{

int read_character = fgetc (input_file);

if (read_character == EOF)

{

if (matched_characters > 0)

{

if (fwrite (find_buffer, sizeof (char), matched_characters, stdout) != matched_characters)

{

perror (program_file_name);

return_value = EXIT_FAILURE;

}

matched_characters = 0;

}

}

else

{

unsigned char character = read_character;

if (character == find_buffer [matched_characters])

{

matched_characters ++;

if (matched_characters == find_buffer_size)

{

if ((replace_buffer_size > 0) && (fwrite (replace_buffer, sizeof (char), replace_buffer_size, stdout) != replace_buffer_size))

{

perror (program_file_name);

return_value = EXIT_FAILURE;

}

matched_characters = 0;

}

}

else

{

if (matched_characters == 0)

{

fputc (character, stdout);

}

else

{

unsigned long int const previous_matched_characters = matched_characters;

unsigned long int unmatched_characters = 0;

bool found_partial_match = false;

do

{

unmatched_characters ++;

matched_characters = 0;

while ((unmatched_characters + matched_characters < previous_matched_characters) && (find_buffer [unmatched_characters + matched_characters] == find_buffer [matched_characters]))

{

matched_characters ++;

}

if (unmatched_characters + matched_characters == previous_matched_characters)

{

if (character == find_buffer [matched_characters])

{

matched_characters ++;

found_partial_match = true;

if ((fwrite (find_buffer, sizeof (char), unmatched_characters, stdout) != unmatched_characters))

{

perror (program_file_name);

return_value = EXIT_FAILURE;

}

}

else if (unmatched_characters == previous_matched_characters)

{

if ((fwrite (find_buffer, sizeof (char), unmatched_characters, stdout) != unmatched_characters))

{

perror (program_file_name);

return_value = EXIT_FAILURE;

}

fputc (character, stdout);

}

}

}

while ((unmatched_characters != previous_matched_characters) && (found_partial_match == false));

}

}

}

}

while ((feof (input_file) == 0) && (ferror (input_file) == 0));

if (input_file_name != NULL)

{

/* Close the input file. */

if (fclose (input_file) != 0)

{

fprintf (stderr, "%s:", program_file_name);

perror (input_file_name);

return_value = EXIT_FAILURE;

}

}

}

return return_value;

}


int main (int const argument_count, char const * const argument_array [])

{

int return_value = EXIT_SUCCESS;

char const * const program_file_name = argument_array [0];

unsigned short int const option_no_more_options = 1 << 0;

unsigned short int const option_help = 1 << 1;

unsigned short int const option_version = 1 << 2;

unsigned short int const option_read_standard_input = 1 << 3;

char const * const option_string_array [] = { "/-", "--", "?", "/?", "/H", "/h", "-?", "-H", "-h", "-help", "--help", "--version", "-" };

unsigned short int const option_value_array [] = { option_no_more_options, option_no_more_options, option_help, option_help, option_help, option_help, option_help, option_help, option_help, option_help, option_help, option_version, option_read_standard_input };

unsigned char const option_string_count = sizeof (option_string_array) / sizeof (option_string_array [0]);

unsigned char const option_value_count = sizeof (option_value_array) / sizeof (option_value_array [0]);

unsigned short int status = 0;

if (option_string_count != option_value_count)

{

unsigned char option_index;

fprintf (stderr, "%s: Internal error: The size of the option value array (%u) does not match the size of the option string array (%u)\n", program_file_name, option_value_count, option_string_count);

fputs ("index: value, string\n", stderr);

for (option_index = 0; (option_index < option_string_count) && (option_index < option_value_count); option_index ++)

{

fprintf (stderr, "%u: %u, %s\n", option_index, option_value_array [option_index], option_string_array [option_index]);

}

if (option_string_count > option_value_count)

{

for (option_index = option_value_count; option_index < option_string_count; option_index ++)

{

fprintf (stderr, "%u: [none], %s\n", option_index, option_string_array [option_index]);

}

}

else

{

for (option_index = option_string_count; option_index < option_value_count; option_index ++)

{

fprintf (stderr, "%u: %u, [none]\n", option_index, option_value_array [option_index]);

}

}

return_value = EXIT_FAILURE;

}

else

{

/* Set options and find and replace strings */

unsigned short int const file_name_arguments = 1 << 6;

unsigned char * find_buffer = NULL;

unsigned char * replace_buffer = NULL;

unsigned long int find_buffer_size = 0;

unsigned long int replace_buffer_size = 0;

int argument_index;

for (argument_index = 1; (argument_index <= argument_count) && ((status & (option_help | option_version)) == 0); argument_index ++)

{

char const * input_file_name = NULL;

if (argument_index == argument_count)

{

if (((status & file_name_arguments) == 0) || ((status & option_read_standard_input) != 0))

{

if (replace_string_in_file (program_file_name, input_file_name, find_buffer, find_buffer_size, replace_buffer, replace_buffer_size) != EXIT_SUCCESS)

{

return_value = EXIT_FAILURE;

}

}

}

else

{

char const * const current_argument = argument_array [argument_index];

unsigned short int const valid_option = 1 << 8;

status &= ~ valid_option;

if ((status & option_no_more_options) == 0)

{

unsigned char option_index;

for (option_index = 0; (option_index < option_string_count) && ((status & valid_option) == 0); option_index ++)

{

if (strcmp (option_string_array [option_index], current_argument) == 0)

{

status |= valid_option;

status |= option_value_array [option_index];

}

}

}

if ((status & valid_option) == 0)

{

if (find_buffer == NULL)

{

find_buffer = malloc ((strlen (current_argument) + 1) * sizeof(char));

if (find_buffer == NULL)

{

perror (program_file_name);

return_value = EXIT_FAILURE;

}

else

{

find_buffer_size = write_string_to_buffer (current_argument, find_buffer);

}

}

else if (replace_buffer == NULL)

{

replace_buffer = malloc ((strlen (current_argument) + 1) * sizeof(char));

if (replace_buffer == NULL)

{

perror (program_file_name);

return_value = EXIT_FAILURE;

}

else

{

replace_buffer_size = write_string_to_buffer (current_argument, replace_buffer);

}

}

else

{

input_file_name = current_argument;

status |= file_name_arguments;

if (replace_string_in_file (program_file_name, input_file_name, find_buffer, find_buffer_size, replace_buffer, replace_buffer_size) != EXIT_SUCCESS)

{

return_value = EXIT_FAILURE;

}

}

}

}

}

/* Print information */

if ((status & option_help) != 0)

{

printf ("Usage: %s [option] [find_string [replace_string [file_name]]]\n", program_file_name);

puts ("Replace one string with another in standard input or in one or more files and print the result.");

puts ("Separate multiple file names with spaces.");

puts ("Use no file name or use the - option to read standard input.");

puts ("Use the -- option to indicate that the arguments that follow it are not options.");

puts ("The find and replace strings can include C-style escape sequences.");

puts ("Use quotation marks around arguments that contain spaces or escape sequences.");

}

else if ((status & option_version) != 0)

{

puts ("rp 1.9");

}

else if (find_buffer == NULL)

{

fprintf (stderr, "%s: No string to find\n", program_file_name);

return_value = EXIT_FAILURE;

}

if (find_buffer != NULL)

{

free (find_buffer);

find_buffer = NULL;

}

if (replace_buffer != NULL)

{

free (replace_buffer);

replace_buffer = NULL;

}

}

return return_value;

}