//
// aegis - project change supervisor
// Copyright (C) 2008, 2010, 2012, 2014 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see .
//
#include
#include
#include
#include
#include
#include
validation_files_comments::~validation_files_comments()
{
}
validation_files_comments::validation_files_comments()
{
}
validation::pointer
validation_files_comments::create(void)
{
return pointer(new validation_files_comments());
}
bool
validation_files_comments::check_branches(void)
const
{
return false;
}
bool
validation_files_comments::check_downloaded(void)
const
{
return false;
}
bool
validation_files_comments::check_foreign_copyright(void)
const
{
return false;
}
bool
validation_files_comments::check_binaries(void)
const
{
return false;
}
bool
validation_files_comments::check(change::pointer cp, fstate_src_ty *src)
{
nstring path(cp->file_path(src));
assert(!path.empty());
if (path.empty())
return true;
//
// figure out what sort of file we are looking at
//
nstring base = path.basename();
nstring base_ = base.downcase();
bool isa_c_file = base.ends_with(".c");
bool isa_cxx_file =
base.ends_with(".C") ||
base_.ends_with(".c++") ||
base_.ends_with(".cc") ||
base_.ends_with(".cpp") ||
base.ends_with(".H") ||
base_.ends_with(".h++") ||
base_.ends_with(".hh") ||
base_.ends_with(".hpp")
;
bool isa_h_file =
base_.ends_with(".h")
;
if (!isa_c_file && !isa_cxx_file && !isa_h_file)
return true;
os_become_orig();
bool ok = true;
input::pointer ip = input_file::open_text(path);
enum state_t
{
state_normal,
state_double_quote,
state_double_quote_escape,
state_single_quote,
state_single_quote_escape,
state_slash,
state_cxx_comment,
state_c_comment,
state_c_comment_begin,
state_c_comment_doxygen,
state_c_comment_star
};
state_t state = state_normal;
int line_number = 1;
for (;;)
{
int ic = ip->getch();
if (ic < 0)
break;
unsigned char c = ic;
if (c == '\n')
++line_number;
switch (state)
{
case state_normal:
//
// This state is for the body of a C or C++ file. We aren't in
// a string, or a character constant, or any kind of comment.
//
switch (c)
{
case '/':
state = state_slash;
break;
case '\'':
state = state_single_quote;
break;
case '"':
state = state_double_quote;
break;
default:
break;
}
break;
case state_slash:
//
// In this state we have seen a slash. It could be the start
// of a C or C++ comment, or just be a division operator.
//
switch (c)
{
case '/':
//
// We have seen the start of a C++ comment.
//
state = state_cxx_comment;
if (isa_h_file && !isa_cxx_file)
{
isa_h_file = 0;
isa_cxx_file = 1;
}
if (isa_c_file)
{
sub_context_ty sc;
sc.var_set_string("File_Name", src->file_name);
sc.var_set_long("Line_Number", line_number);
change_error
(
cp,
&sc,
i18n("$filename: $linenumber: C++ comment in a C file")
);
ok = false;
}
break;
case '*':
//
// We have seen the start of a C comment, but it could be
// a Doxygen introducer, so we can't whine if it's a C++
// file just yet.
//
state = state_c_comment_begin;
break;
default:
//
// One of the division operators.
// No need to change state.
//
state = state_normal;
break;
}
break;
case state_double_quote:
//
// In this state we have seen a double quote, and possibly
// some content. We are waiting for the closing double quote.
//
switch (c)
{
case '\\':
//
// Start of an escape sequence.
//
state = state_double_quote_escape;
break;
case '"':
case '\n':
//
// Normal and abnormal string constant termination.
//
state = state_normal;
break;
default:
//
// Still in the string. No need to change state.
//
break;
}
break;
case state_double_quote_escape:
//
// We throw away the character immediately following the
// backslash. Escape sequences can be longer than this, but
// are uninteresting to the state machine. The only sequences
// which can confuse the state machine are escaped backslash,
// escaped double quote and escaped newline.
//
state = state_double_quote;
break;
case state_single_quote:
//
// In this state we have seen a single quote, and possibly
// some content. We are waiting for the closing single quote.
//
switch (c)
{
case '\\':
//
// Start of an escape sequence.
//
state = state_single_quote_escape;
break;
case '\'':
case '\n':
//
// Normal and abnormal character constant termination.
//
state = state_normal;
break;
default:
//
// Still in the character constant. No need to change state.
//
break;
}
break;
case state_single_quote_escape:
//
// We throw away the character immediately following the
// backslash. Escape sequences can be longer than this, but
// are uninteresting to the state machine. The only sequences
// which can confuse the state machine are escaped backslash,
// escaped single quote and escaped newline.
//
state = state_single_quote;
break;
case state_cxx_comment:
//
// We have seen '/', '/', and possibly some content.
// We are waiting for the newline which finishes the comment.
//
if (c == '\n')
state = state_normal;
break;
case state_c_comment_begin:
//
// We have seen '/' and '*'. We are waiting for '*' which could
// start a Doxygen comment, or anything else which indicates
// the start of a normal C comment.
//
if (c == '*')
state = state_c_comment_doxygen;
else
{
state = state_c_comment;
goto check_c_comment;
}
break;
case state_c_comment_doxygen:
//
// We have seen '/', '*' and '*'.
//
switch (c)
{
case '/':
//
// This is the end of a very short normal C comment.
//
state = state_normal;
goto check_c_comment;
case '*':
//
// This is the start of a very ugly normal C comment.
//
state = state_c_comment_star;
check_c_comment:
if (isa_h_file && !isa_c_file)
{
isa_h_file = false;
isa_c_file = true;
}
if (isa_cxx_file)
{
sub_context_ty sc;
sc.var_set_string("File_Name", src->file_name);
sc.var_set_long("Line_Number", line_number);
change_error
(
cp,
&sc,
i18n("$filename: $linenumber: C comment in a C++ file")
);
ok = false;
}
break;
default:
//
// This is a Doxygen comment. It is allowed in both C and
// C++ files, due to the limitations of Doxygen. Sigh.
//
state = state_c_comment_star;
break;
}
break;
case state_c_comment:
//
// We are in the body of a C comment. We are waiting for a '*'
// which could start the comment terminator.
//
if (c == '*')
state = state_c_comment_star;
break;
case state_c_comment_star:
//
// We have seen a '*' which could preceed a '/' to finish a
// C comment.
//
switch (c)
{
case '/':
//
// C comment terminator.
//
state = state_normal;
break;
case '*':
//
// Almost. The next '/' will end the comment.
//
break;
default:
//
// No, back to the body of the comment.
//
state = state_c_comment;
break;
}
break;
}
}
ip.reset();
os_become_undo();
return ok;
}
// vim: set ts=8 sw=4 et :