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