// // aegis - project change supervisor // Copyright (C) 2007, 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 #include #include #include #include #include // // These patterns must NOT match themselves. // This is why "Free" is broken up. // // Be mindful of the commas between the pattern strings. // static const char *old_addresses[] = { "F" "ree Software Foundation Inc., " "59 Temple Place", "F" "ree Software Foundation Inc., " "675 Mass Ave", "F" "ree Software Foundation Inc., " "675 Massachusetts Ave", "F" "ree Software Foundation Inc., " "675 Mass Avenue", "F" "ree Software Foundation Inc., " "675 Massachusetts Avenue", }; // // This is the current address. // In time I expect this will become yet another "old" address. // static const char current_address[] = "Free Software Foundation, Inc., " "51 Franklin Street, Fifth Floor, " "Boston, MA 02110-1301 USA."; validation_files_fsf_address::~validation_files_fsf_address() { } validation_files_fsf_address::validation_files_fsf_address() { } validation::pointer validation_files_fsf_address::create(void) { return pointer(new validation_files_fsf_address()); } /** * The match function is used to match a buffer full of text against an * address pattern. * Runs of !isalnum characters in the pattern match * runs of !isalnum characters in the text. * * @param buffer * The text to be matched to the pattern. * @param nbytes * The length of the text buffer. * @param pattern * The pattern to be matched to. NUL terminated C string. Runs * of !isalnum characters in the pattern match runs of !isalnum * characters in the text; isalnum characters are literals. * Pattern must start with literal. * @returns * 0 for no match, or the line number within the buffer of the * match */ static int match(const char *buffer, size_t nbytes, const char *pattern) { trace(("%s\n", __PRETTY_FUNCTION__)); const char *begin = buffer; assert(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; trace(("pattern_word = \"%.*s\"\n", (int)pattern_word_len, pattern)); const char *buffer_end = buffer + nbytes; while (buffer < buffer_end) { trace(("buffer = %p", buffer)); const char *w = (const char *) memmem(buffer, buffer_end - buffer, pattern, pattern_word_len); if (!w) break; trace(("w = %p\n", w)); 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++; trace(("pc = '%c'\n", pc)); unsigned char bc = *buf_p++; trace(("bc = '%c'\n", bc)); if (!isalnum(pc)) { if (isalnum(bc)) { is_a_match = false; break; } while (pat_p < pattern_end && !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) { // // figure out line number // int line_number = 1; for (const char *cp = begin; cp < w; ++cp) if (*cp == '\n') ++line_number; return line_number; } buffer = w + 1; } return 0; } bool validation_files_fsf_address::check_binaries(void) const { return false; } bool validation_files_fsf_address::check(change::pointer cp, fstate_src_ty *src) { nstring path(cp->file_path(src)); assert(!path.empty()); if (path.empty()) return true; // // Read the first few kB of the file. // os_become_orig(); input::pointer ip = input_file::open(path); char buffer[1 << 14]; size_t nbytes = ip->read(buffer, sizeof(buffer)); ip.reset(); os_become_undo(); // // Check against each of the old addresses. // for ( const char **pattern_ptr = old_addresses; pattern_ptr < ENDOF(old_addresses); ++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_charstar("Suggest", current_address); sc.var_optional("Suggest"); change_error(cp, &sc, i18n("$filename: old FSF address")); return false; } } return true; } // vim: set ts=8 sw=4 et :