Indent blocks
This simple C program adjusts the indentation of the lines in a file of C or C++ source code according to the levels of the blocks of code. A block of code begins with a left brace { and ends with a right brace }. Indenting the lines of source code according to the level of the block in which each line occurs can help to make source code more readable.
C code
/* To do:
Use temporary file when output file already exists.
Cope with situation where comments begin or end with a line break between / and *.
Provide option to remove line break before braces, but not if the previous character outside a comment was the semicolon (ignoring whitespace).
*/
/* Indent blocks
Adjusts the indentation of the lines in a file of C or C++ source code according to the levels of the blocks of code
Version 1.1 (2015-12-18 revised 2018-02-18) */
#include <stdio.h>
/* Needed for fprintf, fwrite, fputc, fputs, fopen, fseek, ftell, rewind, fread and fclose */
#include <stdlib.h>
/* Needed for malloc and free */
void write_buffer (const char buffer [], const size_t buffer_size, const size_t start_position, const size_t end_position, FILE * file, const char * output_file_name)
{
if (start_position > end_position || start_position >= buffer_size || end_position > buffer_size)
{
/* Should be able to use %Z with size_t */
fprintf (stderr, "Internal error: attempt to write from %lu to %lu in the buffer of size %lu.\n", (unsigned long int) start_position, (unsigned long int) end_position, (unsigned long int) buffer_size);
}
else if (fwrite (&buffer [start_position], sizeof(char), end_position - start_position, file) != end_position - start_position)
{
fprintf (stderr, "Unable to write to the output file: %s\n", output_file_name);
}
return;
}
void write_indent (const unsigned char characters, const char indent_character, FILE * file, const char * output_file_name)
{
unsigned char character;
for (character = 0; character < characters; character ++)
{
if (fputc (indent_character, file) != indent_character)
{
fprintf (stderr, "Unable to write to the output file: %s\n", output_file_name);
}
}
return;
}
int main (int number_of_arguments, char * arguments [])
{
int return_value = 0;
char * input_file_name = NULL, * output_file_name = NULL;
unsigned char indent_characters_for_brace = 1;
char indent_character_for_brace = ' ';
unsigned char indent_characters_for_block = 2;
char indent_character_for_block = ' ';
unsigned char force_line_break_before_left_brace = 0, force_line_break_after_left_brace = 0;
unsigned char force_line_break_before_right_brace = 0, force_line_break_after_right_brace = 0;
unsigned char find_line_break = 0;
char line_break [] = { 0, 0, 0 };
char quiet = 0;
{
int argument;
for (argument = 1; argument < number_of_arguments; argument ++)
{
unsigned char valid_option = 0;
if (arguments [argument][0] == '/' || arguments [argument][0] == '-')
{
/* Looks like an option */
char option_code = arguments [argument][1];
if (option_code != 0)
{
char option_parameter_1 = arguments [argument][2], option_parameter_2 = 0, option_parameter_3 = 0;
if (option_parameter_1 != 0)
{
option_parameter_2 = arguments [argument][3];
if (option_parameter_2 != 0)
{
option_parameter_3 = arguments [argument][4];
}
}
if (option_parameter_1 == 0 && (option_code == 'q' || option_code == 'Q'))
{
valid_option = 1;
quiet = 1;
}
else if (option_parameter_3 == 0 && (option_parameter_2 == 0 || option_parameter_2 == 't' || option_parameter_2 == 'T'))
{
if (option_code == 'b' || option_code == 'B' || option_code == 'i' || option_code == 'I')
{
/* Set number of characters to indent */
if (option_parameter_1 == 0)
{
valid_option = 1;
if (option_code == 'b' || option_code == 'B')
{
indent_characters_for_brace = 0;
}
else
{
indent_characters_for_block = 0;
}
}
else
{
const char parameter_values [] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcedfghijklmnopqrstuvwxyz";
const unsigned char number_of_parameter_values = sizeof (parameter_values);
unsigned char parameter_number;
unsigned char parameter_match_found = 0;
unsigned char parameter_value = 0;
for (parameter_number = 0; parameter_match_found == 0 && parameter_number < number_of_parameter_values; parameter_number ++)
{
if (option_parameter_1 == parameter_values [parameter_number])
{
parameter_match_found = 1;
if (parameter_number >= 36)
{
parameter_value = parameter_number - 26;
}
else
{
parameter_value = parameter_number;
}
}
}
if (parameter_match_found == 1)
{
valid_option = 1;
if (option_code == 'b' || option_code == 'B')
{
indent_characters_for_brace = parameter_value;
}
else
{
indent_characters_for_block = parameter_value;
}
if (option_parameter_2 == 't' || option_parameter_2 == 'T')
{
/* Indent with tab instead of space */
if (option_code == 'b' || option_code == 'B')
{
indent_character_for_brace = '\t';
}
else
{
indent_character_for_block = '\t';
}
}
}
}
}
else if (option_code == 'f' || option_code == 'F')
{
/* Force line breaks before or after braces */
valid_option = 1;
find_line_break = 1;
if (option_parameter_1 == 'b' || option_parameter_1 == 'B')
{
if (option_parameter_2 == 'l' || option_parameter_2 == 'L')
{
force_line_break_before_left_brace = 1;
}
else if (option_parameter_2 == 'r' || option_parameter_2 == 'R')
{
force_line_break_before_right_brace = 1;
}
else
{
force_line_break_before_left_brace = 1;
force_line_break_before_right_brace = 1;
}
}
else if (option_parameter_1 == 'a' || option_parameter_1 == 'A')
{
if (option_parameter_2 == 'l' || option_parameter_2 == 'L')
{
force_line_break_after_left_brace = 1;
}
else if (option_parameter_2 == 'r' || option_parameter_2 == 'R')
{
force_line_break_after_right_brace = 1;
}
else
{
force_line_break_after_left_brace = 1;
force_line_break_after_right_brace = 1;
}
}
else
{
force_line_break_before_left_brace = 1;
force_line_break_after_left_brace = 1;
force_line_break_before_right_brace = 1;
force_line_break_after_right_brace = 1;
}
}
}
}
}
if (valid_option == 0)
{
if (input_file_name == NULL)
{
input_file_name = arguments [argument];
output_file_name = arguments [argument];
}
else
{
output_file_name = arguments [argument];
}
}
}
}
if (input_file_name == NULL)
{
fputs ("Indent blocks - Adjusts the indentation of the lines in a file of C or C++ source code according to the levels of the blocks of code\n", stderr);
fprintf (stderr, "Usage: %s [/b] [/i] [/f] [/q] input_file [output_file]\n", arguments [0]);
fputs ("Use /b to set the additional indentation for lines beginning with a brace to zero. Append a single digit to use that number of spaces. Additionally append the letter t to indent with tab rather than space. The default behaviour is to indent with 1 space, which is equivalent to /b1.\n", stderr);
fputs ("Use /i to set the additional indentation inside a block to zero. Append a single digit to use that number of spaces. Additionally append the letter t to indent with tab rather than space. The default behaviour is to indent with 2 spaces, which is equivalent to /i2.\n", stderr);
fputs ("Use /f to insert a line break before and after each brace when there is not one there already. Append a letter b or a to only insert a line break before or after each brace respectively. Additionally append a letter l or r to insert a line break only before or after a left or right brace. The default behaviour is not to insert line breaks.\n", stderr);
fputs ("Use /q to set quiet mode, which suppresses information about the block levels.\n", stderr);
fputs ("input_file is the input file name.\n", stderr);
fputs ("output_file is the output file name, if different from the input file name.\n", stderr);
return_value = -1;
}
else
{
FILE * file = fopen (input_file_name, "rb");
if (file == NULL)
{
fprintf (stderr, "Unable to open the input file: %s\n", input_file_name);
return_value = -1;
}
else
{
char * buffer;
size_t buffer_size;
fseek (file, 0, SEEK_END);
buffer_size = ftell (file);
buffer = (char *) malloc (sizeof(char) * buffer_size);
if (buffer == NULL)
{
/* Should be able to use %Z with size_t */
fprintf (stderr, "Unable to allocate sufficient memory (%lu bytes) for the buffer in which to store the input file: %s\n", (unsigned long int) buffer_size, input_file_name);
return_value = -1;
}
else
{
rewind (file);
if (fread (buffer, sizeof(char), buffer_size, file) != buffer_size)
{
fprintf (stderr, "Unable to read the input file: %s\n", input_file_name);
free (buffer);
buffer = NULL;
buffer_size = 0;
return_value = -1;
}
if (fclose (file) != 0)
{
fprintf (stderr, "Unable to close the input file: %s\n", input_file_name);
return_value = -1;
}
if (buffer != NULL)
{
file = fopen (output_file_name, "wb");
if (file == NULL)
{
fprintf (stderr, "Unable to open the output file: %s\n", output_file_name);
return_value = -1;
}
else
{
int level = 0, maximum_level = 0, minimum_level = 0;
size_t position, last_position = 0;
unsigned char line_break_length = 0;
unsigned char new_line = 1, brace_at_start_of_line;
if (find_line_break == 1)
{
/* Look for line breaks in input file buffer */
char character = 0;
for (position = 0; position < buffer_size && character != '\n' && character != '\r'; position ++)
{
character = buffer [position];
}
if (character == 0)
{
/* No line breaks in input file buffer so specify a default line break */
line_break [0] = '\n';
line_break_length = 1;
}
else
{
/* Use the first line break in the input file buffer as the default line break */
line_break [0] = character;
line_break_length = 1;
if (position < buffer_size)
{
character = buffer [position];
if ((character == '\n' || character == '\r') && character != line_break [0])
{
/* The first line break in the input file buffer uses two different characters */
line_break [1] = character;
line_break_length = 2;
}
}
}
}
for (position = 0; position < buffer_size; position ++)
{
if (new_line == 1)
{
write_buffer (buffer, buffer_size, last_position, position, file, output_file_name);
if (buffer [position] == ' ' || buffer [position] == '\t')
{
/* Skip existing indentation */
while ((position < buffer_size) && (buffer [position] == ' ' || buffer [position] == '\t'))
{
position ++;
}
}
last_position = position;
if (position < buffer_size && buffer [position] == '}')
{
write_indent ((level - 1) * indent_characters_for_block, indent_character_for_block, file, output_file_name);
write_indent (indent_characters_for_brace, indent_character_for_brace, file, output_file_name);
brace_at_start_of_line = 1;
}
else
{
write_indent (level * indent_characters_for_block, indent_character_for_block, file, output_file_name);
if (position < buffer_size && buffer [position] == '{')
{
write_indent (indent_characters_for_brace, indent_character_for_brace, file, output_file_name);
brace_at_start_of_line = 1;
}
else
{
brace_at_start_of_line = 0;
}
}
new_line = 0;
}
else
{
brace_at_start_of_line = 0;
}
if (position < buffer_size)
{
if (buffer [position] == '"')
{
/* String literal */
do
{
position ++;
if ((position < buffer_size - 1) && buffer [position] == '\\' && (buffer [position + 1] == '\\' || buffer [position + 1] == '"'))
{
position += 2;
}
}
while (position < buffer_size && buffer [position] != '"');
}
else if (buffer [position] == '\'')
{
/* Character literal */
do
{
position ++;
if ((position < buffer_size - 1) && buffer [position] == '\\' && (buffer [position + 1] == '\\' || buffer [position + 1] == '\''))
{
position += 2;
}
}
while (position < buffer_size && buffer [position] != '\'');
}
else if ((position < buffer_size - 1) && buffer [position] == '/' && buffer [position + 1] == '*')
{
/* Block comment */
position += 2;
do
{
position ++;
}
while (position < buffer_size && (buffer [position] != '/' || buffer [position - 1] != '*'));
}
else if ((position < buffer_size - 1) && buffer [position] == '/' && buffer [position + 1] == '/')
{
/* Single line comment */
do
{
position ++;
if (position < buffer_size && buffer [position] == '\\')
{
do
{
position ++;
}
while (position < buffer_size && (buffer [position] == ' ' || buffer [position] == '\\' || buffer [position] == '\t'));
if (position + 1 < buffer_size && buffer [position] == '\r' && buffer [position + 1] == '\n')
{
position += 2;
}
else if (position < buffer_size && (buffer [position] == '\n' || buffer [position] == '\r'))
{
position ++;
}
}
}
while (position < buffer_size && buffer [position] != '\n' && buffer [position] != '\r');
}
else if (buffer [position] == '\n')
{
new_line = 1;
if (position < buffer_size - 1 && buffer [position + 1] == '\r')
{
position ++;
}
}
else if (buffer [position] == '\r')
{
new_line = 1;
if (position < buffer_size - 1 && buffer [position + 1] == '\n')
{
position ++;
}
}
else if (buffer [position] == '{')
{
if (force_line_break_before_left_brace == 1 && brace_at_start_of_line == 0)
{
write_buffer (buffer, buffer_size, last_position, position, file, output_file_name);
last_position = position;
if (fprintf (file, "%s", line_break) != line_break_length)
{
fprintf (stderr, "Unable to write to the output file: %s\n", output_file_name);
return_value = -1;
}
write_indent (level * indent_characters_for_block, indent_character_for_block, file, output_file_name);
write_indent (indent_characters_for_brace, indent_character_for_brace, file, output_file_name);
}
if (force_line_break_after_left_brace == 1 && (position < buffer_size - 1) && buffer [position + 1] != '\n' && buffer [position + 1] != '\r')
{
write_buffer (buffer, buffer_size, last_position, position + 1, file, output_file_name);
last_position = position + 1;
if (fprintf (file, "%s", line_break) != line_break_length)
{
fprintf (stderr, "Unable to write to the output file: %s\n", output_file_name);
return_value = -1;
}
new_line = 1;
}
level ++;
if (quiet == 0 && level > maximum_level)
{
maximum_level = level;
}
}
else if (buffer [position] == '}')
{
level --;
if (quiet == 0 && level < minimum_level)
{
minimum_level = level;
}
if (force_line_break_before_right_brace == 1 && brace_at_start_of_line == 0)
{
write_buffer (buffer, buffer_size, last_position, position, file, output_file_name);
last_position = position;
if (fprintf (file, "%s", line_break) != line_break_length)
{
fprintf (stderr, "Unable to write to the output file: %s\n", output_file_name);
return_value = -1;
}
write_indent (level * indent_characters_for_block, indent_character_for_block, file, output_file_name);
write_indent (indent_characters_for_brace, indent_character_for_brace, file, output_file_name);
}
if (force_line_break_after_right_brace == 1 && (position < buffer_size - 1) && buffer [position + 1] != '\n' && buffer [position + 1] != '\r')
{
write_buffer (buffer, buffer_size, last_position, position + 1, file, output_file_name);
last_position = position + 1;
if (fprintf (file, "%s", line_break) != line_break_length)
{
fprintf (stderr, "Unable to write to the output file: %s\n", output_file_name);
return_value = -1;
}
new_line = 1;
}
}
}
}
if (last_position < buffer_size)
{
write_buffer (buffer, buffer_size, last_position, buffer_size, file, output_file_name);
}
if (fclose (file) != 0)
{
fprintf (stderr, "Unable to close the output file: %s\n", output_file_name);
return_value = -1;
}
free (buffer);
buffer = NULL;
buffer_size = 0;
if (quiet == 0)
{
if (level != 0 || minimum_level != 0)
{
fputs ("Mismatched braces.\n", stdout);
}
if (minimum_level != 0)
{
fprintf (stdout, "Minimum level: %d\n", minimum_level);
}
fprintf (stdout, "Maximum level: %d\n", maximum_level);
}
}
}
}
}
}
return return_value;
}