Annotate block levels
This simple C program adds or removes comments annotating the level at the start and end of each block in a file of C or C++ source code. A block of code begins with a left brace { and ends with a right brace }. This can be helpful when debugging programs with mismatched braces.
C code
/* use temporary file when output file already exists */
/* remove dependencies on string functions and perform operation in a single scan
you can use \ to slice two lines to join / and * when starting or ending a comment - and for // too
*/
/* Annotate block levels
Adds or removes comments annotating the level at the start and end of each block in a file of C or C++ source code
Version 1.6 (2015-08-30 revised 2015-12-19) */
#include <stdio.h>
/* Needed for fputs, fwrite, fprintf, fopen, fseek, ftell, rewind, fread, and fclose */
#include <stdlib.h>
/* Needed for malloc, free, and realloc */
#include <string.h>
/* Needed for strlen and strncmp */
void update_to_current_position (char buffer [], const size_t buffer_size, size_t position, const size_t offset)
{
static size_t last_position = 0, last_offset = 0;
if (last_offset == 0)
{
last_position = position;
}
else
{
if (position + last_offset > buffer_size)
{
fputs ("Internal error: attempt to overflow the buffer when updating the buffer.\n", stderr);
position = buffer_size - last_offset;
}
for ( ; last_position < position; last_position ++)
{
buffer [last_position] = buffer [last_position + last_offset];
}
}
last_offset = offset;
return;
}
void output_to_current_position (const char buffer [], const size_t buffer_size, size_t position, FILE * file, const char * output_file_name)
{
static size_t last_position = 0;
if (position > buffer_size)
{
fputs ("Internal error: attempt to overflow the buffer when writing out the buffer.\n", stderr);
position = buffer_size;
}
if (fwrite (&buffer [last_position], sizeof(char), position - last_position, file) != position - last_position)
{
fprintf (stderr, "Unable to write to the output file: %s\n", output_file_name);
}
last_position = position + 1;
return;
}
int main (int number_of_arguments, char * arguments [])
{
int return_value = 0;
unsigned char remove_annotations = 0, quiet = 0;
char * input_file_name = NULL, * output_file_name = NULL;
{
int argument;
for (argument = 1; argument < number_of_arguments; argument ++)
{
unsigned char valid_option = 0;
if ((arguments [argument][0] == '/' || arguments [argument][0] == '-') && strlen (arguments [argument]) == 2)
{
/* Looks like an option */
char option_code = arguments [argument][1];
if (option_code == 'q' || option_code == 'Q')
{
valid_option = 1;
quiet = 1;
}
else if (option_code == 'r' || option_code == 'R')
{
valid_option = 1;
remove_annotations = 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 ("Annotate block levels - Adds or removes comments annotating the level at the start and end of each block in a file of C or C++ source code.\n", stderr);
fprintf (stderr, "Usage: %s [/q] [/r] input_file [output_file]\n", arguments [0]);
fputs ("Use /q to set quiet mode, which suppresses information about the block levels.\n", stderr);
fputs ("Use /r to remove any existing annotations.\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)
{
const char open_annotation_prefix [] = " /* open level ";
const char close_annotation_prefix [] = " /* close level ";
{
const char * search_strings [] = { open_annotation_prefix , close_annotation_prefix };
const unsigned char number_of_search_strings = sizeof (search_strings) / sizeof (char *);
unsigned char search_string_lengths [number_of_search_strings];
unsigned char count;
unsigned char minimum_search_string_length = 0;
size_t position, offset = 0;
for (count = 0; count < number_of_search_strings; count ++)
{
search_string_lengths [count] = strlen (search_strings [count]);
}
for (count = 0; count < number_of_search_strings; count ++)
{
if (search_string_lengths [count] > 0)
{
if (minimum_search_string_length == 0 || minimum_search_string_length > search_string_lengths [count])
{
minimum_search_string_length= search_string_lengths [count];
}
}
}
for (position = 0; position + offset < buffer_size - minimum_search_string_length; position ++)
{
if (buffer [position + offset] == '"')
{
/* String literal */
do
{
position ++;
if ((position + offset < buffer_size - 1) && buffer [position + offset] == '\\' && (buffer [position + offset + 1] == '\\' || buffer [position + offset + 1] == '"'))
{
position += 2;
}
}
while (position + offset < buffer_size && buffer [position + offset] != '"');
}
else if (buffer [position + offset] == '\'')
{
/* Character literal */
do
{
position ++;
if ((position + offset < buffer_size - 1) && buffer [position + offset] == '\\' && (buffer [position + offset + 1] == '\\' || buffer [position + offset + 1] == '\''))
{
position += 2;
}
}
while (position + offset < buffer_size && buffer [position + offset] != '\'');
}
else if ((position + offset < buffer_size - 1) && buffer [position + offset] == '/' && buffer [position + offset + 1] == '*')
{
/* Block comment */
position += 2;
do
{
position ++;
}
while (position + offset < buffer_size && (buffer [position + offset] != '/' || buffer [position + offset - 1] != '*'));
}
else if ((position + offset < buffer_size - 1) && buffer [position + offset] == '/' && buffer [position + offset + 1] == '/')
{
/* Single line comment */
do
{
position ++;
if (position + offset < buffer_size && buffer [position + offset] == '\\')
{
do
{
position ++;
}
while (position + offset < buffer_size && (buffer [position + offset] == ' ' || buffer [position + offset] == '\\' || buffer [position + offset] == '\t'));
if (position + offset + 1 < buffer_size && buffer [position + offset] == '\r' && buffer [position + offset + 1] == '\n')
{
position += 2;
}
else if (position + offset < buffer_size && (buffer [position + offset] == '\n' || buffer [position + offset] == '\r'))
{
position ++;
}
}
}
while (position + offset < buffer_size && buffer [position + offset] != '\n' && buffer [position + offset] != '\r');
}
else
{
unsigned char found = number_of_search_strings;
for (count = 0; count < number_of_search_strings && found == number_of_search_strings; count ++)
{
if ((position + offset < buffer_size - search_string_lengths [count]) && strncmp (& buffer [position + offset], search_strings [count], search_string_lengths [count]) == 0)
{
found = count;
}
}
if (found < number_of_search_strings)
{
offset += search_string_lengths [found];
while ((position + offset < buffer_size - 1) && buffer [position + offset] != '*' && buffer [position + offset + 1] != '/')
{
offset ++;
}
offset += 2;
update_to_current_position (buffer, buffer_size, position, offset);
}
}
}
if (offset > 0)
{
update_to_current_position (buffer, buffer_size, buffer_size - offset, offset);
buffer_size -= offset;
buffer = (char *) realloc (buffer, buffer_size);
if (buffer == NULL)
{
fputs ("Unable to resize the buffer storing the input file.\n", stderr);
buffer_size = 0;
}
}
}
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;
for (position = 0; position < buffer_size; position ++)
{
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] == '{')
{
if (remove_annotations == 1)
{
level ++;
}
else
{
output_to_current_position (buffer, buffer_size, position, file, output_file_name);
if (fprintf (file, "{%s%d */", open_annotation_prefix, ++ level) == EOF)
{
fprintf (stderr, "Unable to write to the output file: %s\n", output_file_name);
return_value = -1;
}
}
if (quiet == 0 && level > maximum_level)
{
maximum_level = level;
}
}
else if (buffer [position] == '}')
{
if (remove_annotations == 1)
{
level --;
}
else
{
output_to_current_position (buffer, buffer_size, position, file, output_file_name);
if (fprintf (file, "}%s%d */", close_annotation_prefix, level --) == EOF)
{
fprintf (stderr, "Unable to write to the output file: %s\n", output_file_name);
return_value = -1;
}
}
}
if (quiet == 0 && level < minimum_level)
{
minimum_level = level;
}
}
}
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);
}
}
output_to_current_position (buffer, buffer_size, 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;
}
}
}
}
}
}
return return_value;
}