1
0
mirror of https://github.com/UzixLS/KernelEx.git synced 2025-07-19 07:21:20 +03:00
Files
KernelEx/util/prep/prep.cpp
2018-11-03 16:18:57 +03:00

700 lines
16 KiB
C++

/*
* KernelEx
* Copyright (C) 2009, Xeno86
*
* This file is part of KernelEx source code.
*
* KernelEx 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 2 of the License.
*
* KernelEx 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 GNU Make; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#pragma warning(disable:4786)
#include <windows.h>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
#include <exception>
#include <algorithm>
#include <cctype>
using namespace std;
#include "prep.h"
//////////////////////////////////////////////////////////////////////////
vector<export_entry_named> all_exports_named;
vector<export_entry_ordinal> all_exports_ordinal;
stringstream all_declarations;
//////////////////////////////////////////////////////////////////////////
int count(const string& s, char c, size_t pos, size_t epos)
{
int num = 0;
if (epos == string::npos)
epos = s.length();
if (pos < 0 || pos >= epos)
return 0;
for (int i = pos ; i < epos ; i++)
if (s[i] == c)
num++;
return num;
}
//////////////////////////////////////////////////////////////////////////
FileFinder::FileFinder()
{
}
FileFinder::FileFinder(const string& fn)
{
search_for(fn);
}
FileFinder::~FileFinder()
{
}
void FileFinder::search_for(const string& fn)
{
WIN32_FIND_DATA find_data;
files.clear();
pos = 0;
HANDLE h = FindFirstFile(fn.c_str(), &find_data);
if (h == INVALID_HANDLE_VALUE)
return;
do
files.push_back(find_data.cFileName);
while (FindNextFile(h, &find_data));
CloseHandle(h);
stable_sort(files.begin(), files.end());
}
string FileFinder::get_next_file()
{
if (files.empty() || pos >= files.size())
return "";
return files[pos++];
}
//////////////////////////////////////////////////////////////////////////
FileDeclParser::FileDeclParser(const char* file_name) throw(exception)
{
this->file_name = file_name;
ifstream f(file_name);
if (!f)
throw Exception("Failed to open file");
stringstream buf;
buf << f.rdbuf();
buffer = buf.str();
}
void FileDeclParser::parse() throw(exception)
{
size_t pos = 0;
while ((pos = buffer.find("MAKE_EXPORT", pos)) != string::npos)
{
size_t lfpos = buffer.find('\n', pos);
if (lfpos == string::npos)
break;
parse_make_export(buffer.substr(pos, lfpos - pos));
pos += sizeof("MAKE_EXPORT");
}
pos = 0;
while ((pos = buffer.find("FORWARD_TO_UNICOWS(", pos)) != string::npos)
{
size_t lfpos = buffer.find('\n', pos);
if (lfpos == string::npos)
break;
if (!is_containing_line_commented(pos))
parse_forward_to_unicows(buffer.substr(pos, lfpos - pos));
pos = lfpos + 1;
}
int total_exports = exports_named.size() + exports_ordinal.size();
if (total_exports == 0)
{
cout << "File doesn't contain exports" << endl;
return;
}
prefilter();
int dcount = 0;
size_t lpos = 0; //last pos
int nest_level = 0; // nest level
while ((pos = buffer.find('(', lpos)) != string::npos)
{
nest_level += count(buffer, '{', lpos, pos)
- count(buffer, '}', lpos, pos);
lpos = pos + 1;
if (nest_level == 0)
{
if (is_containing_line_commented(pos))
continue;
//at this level we have position of function opening brace '('
//the hard part is finding beginning of function declaration
size_t beg = buffer.find_last_of(";}", pos);
if (beg == string::npos)
beg = 0;
while (is_containing_line_commented(beg) && beg != string::npos)
beg = buffer.find_last_of(";}", beg - 1);
if (beg == string::npos)
beg = 0;
if (beg)
beg++;
size_t end = buffer.find(')', pos);
if (end == string::npos)
continue;
while (is_containing_line_commented(end) && end != string::npos)
end = buffer.find(')', end + 1);
if (end == string::npos)
continue;
end++;
string decl = buffer.substr(beg, end - beg) + ';';
filter_declaration(decl);
int cnt = find_matching_export(decl);
if (!cnt)
continue;
all_declarations << decl << endl;
dcount += cnt;
//cout << decl << endl;
lpos = end + 1;
}
}
vector<export_entry_named>::iterator i1 = exports_named.begin();
vector<export_entry_ordinal>::iterator i2 = exports_ordinal.begin();
while (i1 != exports_named.end())
all_exports_named.push_back(*i1++);
while (i2 != exports_ordinal.end())
all_exports_ordinal.push_back(*i2++);
if (dcount < total_exports)
cout << "!Unresolved exports: " << total_exports - dcount << endl;
}
size_t FileDeclParser::find_line_start(size_t pos)
{
size_t line_start = buffer.rfind('\n', pos);
if (line_start == string::npos)
line_start = 0;
else
line_start++;
return line_start;
}
bool FileDeclParser::is_containing_line_commented(size_t pos)
{
size_t line_start = find_line_start(pos);
string p = buffer.substr(line_start, pos - line_start);
size_t pp;
if ((pp = p.find("//")) != string::npos || (pp = p.find('#')) != string::npos)
{
for (int i = 0 ; i < pp ; i++)
// if there is something before '//' then the line isn't commented
if (p[i] != ' ' && p[i] != '\t')
return false;
return true;
}
return false;
}
void FileDeclParser::parse_make_export(const string& line) throw(exception)
{
size_t pos = line.find("MAKE_EXPORT");
if (pos == string::npos)
throw Exception("MAKE_EXPORT tag not found");
pos += sizeof("MAKE_EXPORT");
pos = line.find_first_not_of(" \t", pos);
if (pos == string::npos)
throw Exception("Invalid MAKE_EXPORT tag");
size_t pos2 = line.find('=', pos);
if (pos2 == string::npos)
throw Exception("Invalid MAKE_EXPORT tag");
string source_name = line.substr(pos, pos2 - pos);
pos = pos2 + 1;
pos2 = line.find_first_of(" \t", pos);
if (line.compare(pos, sizeof("ordinal") - 1, "ordinal"))
{
export_entry_named ee;
ee.source_name = source_name;
ee.export_name = line.substr(pos, pos2 - pos);
exports_named.push_back(ee);
}
else
{
export_entry_ordinal ee;
ee.source_name = source_name;
ee.ordinal = strtol(line.substr(pos + sizeof("ordinal") - 1).c_str(), NULL, 0);
exports_ordinal.push_back(ee);
}
}
void FileDeclParser::parse_forward_to_unicows(const string& line) throw(exception)
{
size_t pos = line.find("FORWARD_TO_UNICOWS(");
if (pos == string::npos)
throw Exception("FORWARD_TO_UNICOWS(");
pos += sizeof("FORWARD_TO_UNICOWS(") - 1;
size_t pos2 = line.find(')', pos);
if (pos2 == string::npos)
throw Exception("Invalid FORWARD_TO_UNICOWS tag");
string name = line.substr(pos, pos2 - pos);
export_entry_named ee;
ee.source_name = name + "_fwd";
ee.export_name = name;
all_exports_named.push_back(ee);
all_declarations << "FWDPROC " + name + "_fwd;" << endl;
}
int FileDeclParser::find_matching_export(const string& s)
{
int count = 0;
//try named exports first
vector<export_entry_named>::iterator itn = exports_named.begin();
while (itn != exports_named.end())
{
if (s.find(itn->source_name) != string::npos)
count++;
itn++;
}
//then try ordinal exports
vector<export_entry_ordinal>::iterator ito = exports_ordinal.begin();
while (ito != exports_ordinal.end())
{
if (s.find(ito->source_name) != string::npos)
count++;
ito++;
}
return count;
}
void FileDeclParser::prefilter() throw(exception)
{
//erase C-style comments
size_t pos;
while ((pos = buffer.find("/*")) != string::npos)
{
size_t pos2 = buffer.find("*/", pos + 1);
if (pos2 == string::npos)
throw Exception("No matching C-style comment closing");
buffer.erase(pos, pos2 - pos + sizeof("*/") - 1);
}
}
void FileDeclParser::filter_declaration(string& s)
{
int i = 0;
//erase precompiler directives
size_t pos;
while ((pos = s.find('#')) != string::npos)
{
size_t p2 = s.find('\n', pos + 1);
if (p2 != string::npos)
p2 -= pos;
s.erase(pos, p2);
}
//erase C++ style comments
while ((pos = s.find("//")) != string::npos)
{
size_t p2 = s.find('\n', pos + 1);
if (p2 != string::npos)
p2 -= pos;
s.erase(pos, p2);
}
//erase "__declspec(naked)"
while ((pos = s.find("__declspec(naked)")) != string::npos)
s.erase(pos, sizeof("__declspec(naked)"));
//replace tabs and newlines with spaces
for (i = 0 ; i < s.length() ; i++)
if (s[i] == '\t' || s[i] == '\n')
s[i] = ' ';
//erase leading spaces
while (s[0] == ' ')
s.erase(0, 1);
//erase spaces after bracket
pos = s.find('(');
while (s[pos + 1] == ' ')
s.erase(pos + 1, 1);
//erase spaces before bracket
pos = s.rfind(')');
while (s[pos - 1] == ' ')
{
s.erase(pos - 1, 1);
pos--;
}
//erase multiple spaces
i = 0;
while (i < s.length())
{
if (s[i] == ' ')
while (i + 1 < s.length() && s[i + 1] == ' ')
s.erase(i + 1, 1);
i++;
}
}
//////////////////////////////////////////////////////////////////////////
StubsParser::StubsParser(const char* file_name) throw(exception) : FileDeclParser(file_name)
{
}
void StubsParser::parse() throw(exception)
{
prefilter();
size_t pos = 0;
size_t lpos = 0;
while ((pos = buffer.find("UNIMPL_FUNC", lpos)) != string::npos)
{
size_t beg = buffer.find('(', pos);
if (beg == string::npos)
continue;
beg++;
while (isspace(buffer[beg]))
beg++;
size_t end = buffer.find(',', beg + 1);
if (end == string::npos)
continue;
end--;
while (isspace(buffer[end]))
end--;
string name = buffer.substr(beg, end - beg + 1);
string decl = "STUB " + name + "_stub" + ';';
all_declarations << decl << endl;
export_entry_named ee;
ee.export_name = name;
ee.source_name = name + "_stub";
all_exports_named.push_back(ee);
lpos = end + 1;
}
}
//////////////////////////////////////////////////////////////////////////
void write_named_exports(ostream& o)
{
vector<export_entry_named>::iterator it = all_exports_named.begin();
while (it != all_exports_named.end())
{
o << "\tDECL_API(\"" << it->export_name << "\", "
<< it->source_name << ")," << endl;
it++;
}
}
void write_ordinal_exports(ostream& o)
{
vector<export_entry_ordinal>::iterator it = all_exports_ordinal.begin();
while (it != all_exports_ordinal.end())
{
o << "\tDECL_API(" << it->ordinal << ", "
<< it->source_name << ")," << endl;
it++;
}
}
void dump_all()
{
cout << "*** function declarations:" << endl;
cout << all_declarations.rdbuf();
cout << "*** named exports:" << endl;
write_named_exports(cout);
cout << "*** ordinal exports:" << endl;
write_ordinal_exports(cout);
}
void replace_if_changed(const string& out, const string& in)
{
fstream f;
stringstream buf;
string in1;
string in2;
f.open(out.c_str(), fstream::in);
if (!f)
throw Exception("Failed to open file");
buf << f.rdbuf();
in1 = buf.str();
f.close();
f.open(in.c_str(), fstream::in);
if (!f)
throw Exception("Failed to open file");
buf.clear();
buf.str("");
buf << f.rdbuf();
in2 = buf.str();
f.close();
if (in1.compare(in2))
{
f.open(out.c_str(), ios::out | ios::trunc);
if (!f)
throw Exception("Failed to open file");
f << in2;
f.close();
}
remove(in.c_str());
}
void work()
{
ifstream dirlist("dirlist");
if (!dirlist)
throw Exception("Couldn't open dirlist");
while (!dirlist.eof())
{
fstream out_file;
stringstream buf;
string path;
string file;
FileFinder ff;
string line;
all_exports_named.clear();
all_exports_ordinal.clear();
all_declarations.clear();
all_declarations.str("");
getline(dirlist, path);
//skip empty lines
if (path.empty())
continue;
path += '\\';
cout << "Processing directory: '" << path << '\'' << endl;
ff.search_for(path + "*.c");
//read declarations
while (!(file = ff.get_next_file()).empty())
{
FileDeclParser* parser;
//if (file.find("_apilist.") != string::npos)
// continue;
file = path + file;
cout << "Parsing file: '" << file << '\'' << endl;
if (file.find("_stubs.") != string::npos)
parser = new StubsParser(file.c_str());
else
parser = new FileDeclParser(file.c_str());
parser->parse();
delete parser;
}
stable_sort(all_exports_named.begin(), all_exports_named.end());
stable_sort(all_exports_ordinal.begin(), all_exports_ordinal.end());
//write output defs
ff.search_for(path + "_*_apilist.c");
file = ff.get_next_file();
if (file.empty())
throw Exception("Couldn't find output def file");
file = path + file;
cout << "Definitions output: '" << file << '\'' << endl;
out_file.open(file.c_str(), fstream::in);
if (!out_file.is_open())
throw Exception("Failed to open output def file for read");
buf << out_file.rdbuf();
out_file.close();
out_file.open((file + ".tmp").c_str(), ios::out | ios::trunc);
if (!out_file.is_open())
throw Exception("Failed to open output def file for write");
while (true)
{
getline(buf, line);
if (0 == line.compare("/*** AUTOGENERATED APILIST NAMED EXPORTS BEGIN ***/"))
{
out_file << line << endl;
while (true)
{
getline(buf,line);
if (0 == line.compare("/*** AUTOGENERATED APILIST NAMED EXPORTS END ***/"))
break;
}
write_named_exports(out_file);
}
else if (0 == line.compare("/*** AUTOGENERATED APILIST ORDINAL EXPORTS BEGIN ***/"))
{
out_file << line << endl;
while (true)
{
getline(buf,line);
if (0 == line.compare("/*** AUTOGENERATED APILIST ORDINAL EXPORTS END ***/"))
break;
}
write_ordinal_exports(out_file);
}
out_file << line;
if (!buf.eof())
out_file << endl;
else
break;
}
out_file.close();
replace_if_changed(file, file + ".tmp");
//write output decls
ff.search_for(path + "_*_apilist.h");
file = ff.get_next_file();
if (file.empty())
throw Exception("Couldn't find output decl file");
file = path + file;
cout << "Declarations output: '" << file << '\'' << endl;
out_file.open(file.c_str(), fstream::in);
if (!out_file.is_open())
throw Exception("Failed to open output decl file for read");
buf.clear();
buf.str("");
buf << out_file.rdbuf();
out_file.close();
out_file.open((file + ".tmp").c_str(), ios::out | ios::trunc);
if (!out_file.is_open())
throw Exception("Failed to open output decl file for write");
while (true)
{
getline(buf, line);
if (0 == line.compare("/*** AUTOGENERATED APILIST DECLARATIONS BEGIN ***/"))
{
out_file << line << endl;
while (true)
{
getline(buf,line);
if (0 == line.compare("/*** AUTOGENERATED APILIST DECLARATIONS END ***/"))
break;
}
all_declarations.peek();
if (all_declarations.good())
out_file << all_declarations.rdbuf();
}
out_file << line;
if (!buf.eof())
out_file << endl;
else
break;
}
out_file.close();
replace_if_changed(file, file + ".tmp");
}
}
int main(int argc, char* argv[])
{
if (argc > 2)
{
cout << "Invalid parameters!" << endl;
return 1;
}
if (argc == 2)
if (!SetCurrentDirectory(argv[1]))
{
cout << "Failed to change directory" << endl;
return 1;
}
try
{
work();
}
catch (const exception& e)
{
cout << "Exception: " << e.what() << endl;
return 1;
}
return 0;
}