// File Name: license_update.cxx // Author: Peter Stevenson // Email Address: address@hidden // Description: Program to update KFV wiki with information gleaned from // MODULE_LICENSE and EXPORT_SYMBOL_GPL calls in the kernel. // Last Changed: May 7, 2008 #include // Provides find #include // Provides assert #include // Provides EXIT_SUCCESS and system #include // Provides ifstream #include // Provides cin and cout #include // Provides getline and string #include // Provides vector using namespace std; // Structure that keeps license information relating to a specific kernel file // Uses string struct FileInfo { bool to_update; string file_name, function_license, relevant_code, url, wiki_license; }; // Structure that keeps information about a particular type of license // Uses vector struct LicenseInfo { string name; vector relevant_code, to_update, not_to_update; }; void compare_licenses(FileInfo& record, vector& licenses); // Precondition: record has been created by get_kernel_data. There must be a // record in licenses that corresponds to the license in record. // Postcondition: record.to_update is true if record.function_license is more // specific than record.wiki_license. void find_replace(string& str, string to_replace, string replacement); // Precondition: None. // Postcondition: All instances of to_replace in str have been changed to // replacement. vector get_kernel_data(const string PATH, const string DIRECTORY, const string URL); // Precondition: PATH is the full path name to the kernel, DIRECTORY is the // name of the directory to be searched, and URL is the prefix for the // gNewSense wiki KFV site that is to be updated. // Postcondition: Returns a vector of FileInfos. Each file in the kernel that // has a MODULE_LICENSE or an EXPORT_SYMBOL_GPL call in it now has a // corresponding record in the returned vector. The members file_name, // relevant_code and url are filled with the appropriate data. void get_wiki_data(FileInfo record, const string FILE_NAME); // Precondition: record has been produced by get_kernel_data. FILE_NAME is a // valid name of a file, although the file does not necessarily exist. // Postcondition: The license information in record.url has been saved in the // file called FILE_NAME. void get_url(FileInfo& record, const string DIRECTORY, const string URL); // Precondition: record.file_name is a correct full path to a file in the // kernel, DIRECTORY is the directory in the kernel you want to search, and // URL is the prefix for the gNewSense wiki KFV site that is to be updated. // Postcondition: record.url is a url to the gNewSense wiki KFV page // corresponding to record.file_name. string get_wiki_license(const string FILE_NAME); // Precondition: FILE_NAME is the name of a text file in the current directory. // Returns the first line of the file called FILE_NAME. void identify_license(FileInfo& record, vector& licenses); // Precondition: record has been produced by get_kernel_data. // Postcondition: record.function_license is filled with the appropriate license // based on record.relevant_code. A LicenseInfo record in licenses has either // been created or updated with data from record. bool is_only_export_statements(string relevant_code); // Returns true if every line of relevant code begins with "EXPORT_SYMBOL_GPL". // Otherwise returns false. string process_input(char input); // Returns a proper license name from condensed user input. Uses same code as // kfv.el. void update_data(FileInfo& record, const string FILE_NAME, vector& licenses); // Precondition: record has been produced by get_kernel_data. FILE_NAME is the // name of a file that contains the license information from the record.url. // Postcondition: record.wiki_license has been filled by the license on the KFV // wiki site. record.function_license has been filled with the appropriate // license based on record.relevant_code. Finally, if the function license is // more specific than the wiki license, the wiki license has been replaced by // the function license. void update_wiki(FileInfo record, const string FILE_NAME); // Precondition: record has been produced by get_kernel_data and has had // update_data run on it. FILE_NAME is the name of a text file in the // current directory. // Postcondition: The wiki site record.url has been updated based on the // information from the kernel file. // Uses string and vector int main( ) { const string DIRECTORY = "drivers", PATH = "/usr/local/src/linux-2.6.24_12.22/", URL = "http://wiki.gnewsense.org/Kernel/Ubuntu-hardy-linux-2-6-24-12-22--", WIKI_LICENSE_FILE_NAME = "tmp"; vector data = get_kernel_data(PATH, DIRECTORY, URL); vector licenses; vector::iterator iter; for (iter = data.begin( ); iter != data.end( ); iter++) { (*iter).to_update = false; get_wiki_data(*iter, WIKI_LICENSE_FILE_NAME); update_data(*iter, WIKI_LICENSE_FILE_NAME, licenses); update_wiki(*iter, WIKI_LICENSE_FILE_NAME); } return(EXIT_SUCCESS); } // Uses string and vector void compare_licenses(FileInfo& record, vector& licenses) { vector::iterator iter; vector::iterator i2; string input; for (iter = licenses.begin( ); iter != licenses.end( ); iter++) if (record.function_license == (*iter).name) { for (i2 = (*iter).to_update.begin( ); i2 != (*iter).to_update.end( ); i2++) if (record.wiki_license == *i2) { record.to_update = true; return; } for (i2 = (*iter).not_to_update.begin( ); i2 != (*iter).not_to_update.end( ); i2++) if (record.wiki_license == *i2) return; break; } cout << "File name is: " << record.file_name << endl << "Function license is: " << record.function_license << endl << "Wiki license is: " << record.wiki_license << endl << "Modify?"; cin >> input; if (input == "n") (*iter).not_to_update.push_back(record.wiki_license); else { (*iter).to_update.push_back(record.wiki_license); record.to_update = true; } } // Uses string void find_replace(string& str, string to_replace, string replacement) { string::size_type pos = 0; while ((pos = str.find(to_replace)) != string::npos) { str.replace(pos, to_replace.size( ), replacement); pos++; } return; } // Uses cassert, cstdlib, fstream, string and vector vector get_kernel_data(const string PATH, const string DIRECTORY, const string URL) { vector data; const string TMP_FILE_NAME = "tmp", LICENSES_FILE_NAME = "licenses"; string command = "echo \"MODULE_LICENSE\nEXPORT_SYMBOL_GPL\nDRIVER_LICENSE\" > " + LICENSES_FILE_NAME, next; ifstream in_stream; FileInfo record; system(command.c_str( )); command = "fgrep -rHf " + LICENSES_FILE_NAME + " " + PATH + DIRECTORY + " > " + TMP_FILE_NAME; system(command.c_str( )); in_stream.open(TMP_FILE_NAME.c_str( )); assert(!in_stream.fail( )); while (getline(in_stream, next, ':')) { if (data.size( ) == 0) { record.file_name = next; getline(in_stream, record.relevant_code); get_url(record, DIRECTORY, URL); data.push_back(record); } else if (next != data.back( ).file_name) { record.file_name = next; getline(in_stream, record.relevant_code); get_url(record, DIRECTORY, URL); data.push_back(record); } else { getline(in_stream, next); data.back( ).relevant_code.append("\n" + next); } } in_stream.close( ); command = "rm -f " + TMP_FILE_NAME + " " + LICENSES_FILE_NAME; system(command.c_str( )); return(data); } // Uses algorithm, iostream, string and vector string get_license(string relevant_code, vector& licenses) { } // Uses string void get_url(FileInfo& record, const string DIRECTORY, const string URL) { string file2url; string::size_type pos; file2url = record.file_name; pos = file2url.find(DIRECTORY); file2url.erase(0, pos); find_replace(file2url, "/", "--"); find_replace(file2url, ".", "-"); find_replace(file2url, "_", "-"); file2url = URL + file2url; record.url = file2url; return; } // Uses cstdlib and string void get_wiki_data(FileInfo record, const string FILE_NAME) { string command = "wget -qO \"/dev/null\" --no-proxy --keep-session-cookies --save-cookies=new-cookies.txt --post-data='authid=petercstevenson&authpw=pet4spik' 'http://wiki.gnewsense.org/Main/HomePage?action=login' && mv new-cookies.txt cookies.txt && wget --no-proxy --load-cookies=cookies.txt -qO- '" + record.url + "?action=edit' | tr \"\\n\" \"\\024\" | grep -o \"\" | sed 's/<[^>]*>//g' | tr \"\\024\" \"\\n\" | unhtml | ascii2uni -qa D > tmp"; system(command.c_str( )); } // Uses cassert, fstream and string string get_wiki_license(const string FILE_NAME) { string license; ifstream in_stream; in_stream.open(FILE_NAME.c_str( )); assert(!in_stream.fail( )); getline(in_stream, license); in_stream.close( ); return(license); } // Uses iostream, string and vector void identify_license(FileInfo& record, vector& licenses) { bool is_new = true; char input; vector::iterator iter; if (is_only_export_statements(record.relevant_code)) record.function_license = "GPL (no version specified)"; else { for (iter = licenses.begin( ); iter != licenses.end( ); iter++) if (find((*iter).relevant_code.begin( ), (*iter).relevant_code.end( ), record.relevant_code) != (*iter).relevant_code.end( )) { record.function_license = (*iter).name; return; } do { cout << "\nRelevant code is:\n" << record.relevant_code << endl << "Please type in the license code or '?' for help: "; cin >> input; if (input == '?') cout << "0: GPLv3\n" << "1: GPLv2 or later\n" << "2: GPLv2\n" << "3: GPLv1\n" << "4: LGPLv3\n" << "5: LGPLv2.1\n" << "6: LGPLv2.0\n" << "7: GFDL v1.2\n" << "8: GFDL v1.1\n" << "9: Modified BSD license (3-clause)\n" << "a: FreeBSD license (2-clause)\n" << "b: OpenIB.org BSD license\n" << "c: GPLv2 / Modified BSD license\n" << "d: GPLv2 / FreeBSD license\n" << "e: GPLv2 w/in kernel; otherwise Modified BSD license\n" << "f: GPLv2 / MPL v1.1\n" << "g: GPL / FreeBSD license\n" << "h: X11 (aka MIT) License\n" << "i: CPL 1.0 / Modified BSD license / GPLv2\n" << "j: GPLv2 + FreeBSD license w/in kernel; otherwise FreeBSD license\n" << "k: Public Domain\n" << "l: No license, so assumed to be GPLv2.\n" << "m: GPL (no version specified)\n" << "n: Other software license (free)\n" << "o: REPLACE WITH CUSTOM LICENSE TEXT\n" << "p: Other software license (non-free)\n"; } while (input == '?'); record.function_license = process_input(input); } for (iter = licenses.begin( ); iter != licenses.end( ); iter++) if ((*iter).name == record.function_license) { (*iter).relevant_code.push_back(record.relevant_code); return; } vector new_code; LicenseInfo new_record; new_code.push_back(record.relevant_code); new_record.name = record.function_license; new_record.relevant_code = new_code; licenses.push_back(new_record); } // Uses string bool is_only_export_statements(string relevant_code) { string::size_type pos = 0; bool is_all_export = true; do { if ((relevant_code.size( ) - pos) > 17) if (relevant_code.substr(pos, 17) != "EXPORT_SYMBOL_GPL") is_all_export = false; pos = relevant_code.find("\n", pos); } while (pos++ < relevant_code.size( )); return(is_all_export); } // Uses string string process_input(char input) { switch(input) { case '0': return("GPLv3"); case '1': return("GPLv2 or later"); case '2': return("GPLv2"); case '3': return("GPLv1"); case '4': return("LGPLv3"); case '5': return("LGPLv2.1"); case '6': return("LGPLv2.0"); case '7': return("GFDL v1.2"); case '8': return("GFDL v1.1"); case '9': return("Modified BSD license (3-clause)"); case 'a': return("FreeBSD license (2-clause)"); case 'b': return("OpenIB.org BSD license"); case 'c': return("GPLv2 / Modified BSD license"); case 'd': return("GPLv2 / FreeBSD license"); case 'e': return("GPLv2 w/in kernel; otherwise Modified BSD license"); case 'f': return("GPLv2 / MPL v1.1"); case 'g': return("GPL / FreeBSD license"); case 'h': return("X11 (aka MIT) License"); case 'i': return("CPL 1.0 / Modified BSD license / GPLv2"); case 'j': return("GPLv2 + FreeBSD license w/in kernel; otherwise FreeBSD license"); case 'k': return("Public Domain"); case 'l': return("No license, so assumed to be GPLv2."); case 'm': return("GPL (no version specified)"); case 'n': return("Other software license (free)"); case 'o': return("REPLACE WITH CUSTOM LICENSE TEXT"); case 'p': return("Other software license (non-free)"); default: return("REPLACE WITH CUSTOM LICENSE TEXT"); } } // Uses string and vector void update_data(FileInfo& record, const string FILE_NAME, vector& licenses) { record.wiki_license = get_wiki_license(FILE_NAME); identify_license(record, licenses); compare_licenses(record, licenses); } // Uses cstdlib and string void update_wiki(FileInfo record, const string FILE_NAME) { string command, license; string::size_type pos = 0; if (record.to_update) { command = "sed -i \"1c \\ " + record.function_license + "\" " + FILE_NAME; system(command.c_str( )); license = record.function_license; } else license = record.wiki_license; do { record.relevant_code.insert(pos, " "); pos = record.relevant_code.find("\n", pos); } while (pos++ < record.relevant_code.size( )); command = "echo \"" + record.relevant_code + "\" >> " + FILE_NAME; system(command.c_str( )); command = "echo \"author=petercstevenson&csum=" + record.wiki_license + "&post= Save &text= \" > post-file"; system(command.c_str( )); command = "cat " + FILE_NAME + " >> post-file"; system(command.c_str( )); command = "wget -qO \"/dev/null\" --no-proxy --load-cookies=cookies.txt --post-file='./post-file' '" + record.url + "?action=edit'"; system(command.c_str( )); }