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;

}