//
// aegis - project change supervisor
// Copyright (C) 2007, 2008 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 // for assert
#include
#include
#include
#include
#include
#include
validation_files_gpl_version::~validation_files_gpl_version()
{
}
validation_files_gpl_version::validation_files_gpl_version(int a_version) :
version(a_version)
{
}
//
// Be mindful of the commas between the pattern strings.
//
static const char *notice_patterns[] =
{
"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 * of the "
"License, or (at your option) any later version.",
"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, version *",
"This program is free software: you can redistribute it and/or "
"modify it under the terms of the GNU General Public License, "
"version *, as published by the Free Software Foundation.",
};
int
validation_files_gpl_version::match(const char *text, long nbytes,
const char *pattern)
const
{
assert(isalnum(pattern[0]));
size_t pattern_nbytes = strlen(pattern);
const char *pattern_end = pattern + pattern_nbytes;
//
// Find length of first pattern word.
// We will use the first word to eliminate most false positives.
//
size_t pattern_word_len = 1;
while
(
pattern_word_len < pattern_nbytes
&&
isalnum((unsigned char)pattern[pattern_word_len])
)
++pattern_word_len;
const char *buffer = text;
const char *buffer_end = text + nbytes;
const char *start_of_number = 0;
while (buffer < buffer_end)
{
const char *w =
(const char *)
memmem(buffer, buffer_end - buffer, pattern, pattern_word_len);
if (!w)
break;
const char *buf_p = w + pattern_word_len;
const char *pat_p = pattern + pattern_word_len;
bool is_a_match = true;
while (pat_p < pattern_end)
{
if (buf_p >= buffer_end)
{
is_a_match = false;
break;
}
unsigned char pc = *pat_p++;
unsigned char bc = *buf_p++;
if (pc == '*')
{
start_of_number = buf_p - 1;
if (!isdigit(bc))
{
return 0;
}
int n = bc - '0';
for (;;)
{
bc = *buf_p;
if (!isdigit(bc))
break;
n = n * 10 + bc - '0';
++buf_p;
}
if (n == version)
{
// no need to change anything
return 0;
}
}
else if (!isalnum(pc))
{
if (isalnum(bc))
{
is_a_match = false;
break;
}
while
(
pat_p < pattern_end
&&
*pat_p != '*'
&&
!isalnum((unsigned char)*pat_p)
)
++pat_p;
for (;;)
{
if (buf_p >= buffer_end)
break;
bc = *buf_p;
if
(
bc == '.'
&&
buf_p + 3 <= buffer_end
&&
buf_p[1] == 'b'
&&
buf_p[2] == 'r'
)
{
// assume this is a groff ".br" command
// to separate address lines
buf_p += 3;
continue;
}
if
(
bc == '<'
&&
buf_p + 4 <= buffer_end
&&
(buf_p[1] == 'b' || buf_p[1] == 'B')
&&
(buf_p[2] == 'r' || buf_p[2] == 'R')
&&
buf_p[3] == '>'
)
{
// assume this is a HTML "
" element
// to separate address lines
buf_p += 4;
continue;
}
if
(
bc == 'd'
&&
buf_p + 3 <= buffer_end
&&
buf_p[1] == 'n'
&&
buf_p[2] == 'l'
)
{
// assume this is an M4 comment (like
// configure.ac uses).
buf_p += 3;
continue;
}
if (isalnum(bc))
break;
++buf_p;
}
}
else
{
if (isupper(pc))
pc = tolower(pc);
if (isupper(bc))
bc = tolower(bc);
if (pc != bc)
{
is_a_match = false;
break;
}
}
}
if (is_a_match)
{
int line_number = 1;
for (buffer = text; buffer < start_of_number; ++buffer)
{
if (*buffer == '\n')
++line_number;
}
return line_number;
}
buffer = w + 1;
}
return 0;
}
bool
validation_files_gpl_version::check(change::pointer cp, fstate_src_ty *src)
{
nstring path(change_file_path(cp, src));
assert(!path.empty());
if (path.empty())
return true;
//
// Read the first few kB of the file.
//
os_become_orig();
input ip = input_file_open(path);
char buffer[1 << 14];
size_t nbytes = ip->read(buffer, sizeof(buffer));
ip.close();
os_become_undo();
//
// Check against each of the patterns
//
for
(
const char **pattern_ptr = notice_patterns;
pattern_ptr < ENDOF(notice_patterns);
++pattern_ptr
)
{
int line_number = match(buffer, nbytes, *pattern_ptr);
if (line_number > 0)
{
sub_context_ty sc;
sc.var_set_format
(
"File_Name",
"%s: %d",
src->file_name->str_text,
line_number
);
sc.var_set_long("Suggest", version);
sc.var_optional("Suggest");
change_error(cp, &sc, i18n("$filename: old GPL version"));
return false;
}
}
return true;
}