/*
 * Copyright 2004-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */
 
 
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <list>
#include <stack>
#include <string>
#include <string.h>
#include <vector>
 
#include "gensyscalls_common.h"
 
#include "arch_gensyscalls.h"
	// for the alignment type macros (only for the type names)
 
 
// macro trickery to create a string literal
#define MAKE_STRING(x)	#x
#define EVAL_MACRO(macro, x) macro(x)
 
 
const char* kUsage =
	"Usage: gensyscallinfos <header> <syscall infos> <syscall types sizes>\n"
	"\n"
	"Given the (preprocessed) header file that defines the syscall prototypes "
		"the\n"
	"command generates a source file consisting of syscall infos, which is "
		"needed\n"
	"to build gensyscalls, which in turn generates the assembly syscall\n"
	"definitions and code for the kernelland syscall dispatcher.\n"
	"\n"
	"  <header>               - Input: The preprocessed header file with the\n"
	"                           syscall prototypes.\n"
	"  <syscall infos>        - Output: The syscall infos source file needed "
		"to\n"
	"                           build gensyscalls.\n"
	"  <syscall types sizes>  - Output: A source file that will by another "
		"build\n"
	"                           step turned into a header file included by\n"
	"                           <syscall infos>.\n";
 
 
static void
print_usage(bool error)
{
	fprintf((error ? stderr : stdout), kUsage);
}
 
 
struct Type {
	Type(const char* type) : type(type) {}
	Type(const string& type) : type(type) {}
 
	string type;
};
 
 
struct NamedType : Type {
	NamedType(const char* type, const char* name)
		: Type(type), name(name) {}
	NamedType(const string& type, const string& name)
		: Type(type), name(name) {}
 
	string name;
};
 
 
class Function {
public:
	Function() : fReturnType("") {}
 
	void SetName(const string& name)
	{
		fName = name;
	}
 
	const string& GetName() const
	{
		return fName;
	}
 
	void AddParameter(const NamedType& type)
	{
		fParameters.push_back(type);
	}
 
	int CountParameters() const
	{
		return fParameters.size();
	}
 
	const NamedType& ParameterAt(int index) const
	{
		return fParameters[index];
	}
 
	void SetReturnType(const Type& type)
	{
		fReturnType = type;
	}
 
	const Type& GetReturnType() const
	{
		return fReturnType;
	}
 
protected:
	string				fName;
	vector<NamedType>	fParameters;
	Type				fReturnType;
};
 
 
class Syscall : public Function {
public:
	string GetKernelName() const
	{
		int baseIndex = 0;
		if (fName.find("_kern_") == 0)
			baseIndex = strlen("_kern_");
		else if (fName.find("sys_") == 0)
			baseIndex = strlen("sys_");
		string kernelName("_user_");
		kernelName.append(string(fName, baseIndex));
		return kernelName;
	}
};
 
 
class Tokenizer {
public:
	Tokenizer(istream& input)
		: fInput(input),
		  fHasCurrent(false)
	{
	}
 
	string GetCurrentToken()
	{
		if (!fHasCurrent)
			throw Exception("GetCurrentToken(): No current token!");
		return fTokens.front();
	}
 
	string GetNextToken()
	{
		return GetNextToken(NULL);
	}
 
	string GetNextToken(stack<string>& skippedTokens)
	{
		return GetNextToken(&skippedTokens);
	}
 
	string GetNextToken(stack<string>* skippedTokens)
	{
		if (fHasCurrent) {
			fTokens.pop_front();
			fHasCurrent = false;
		}
		while (fTokens.empty())
			_ReadLine();
		fHasCurrent = true;
		if (skippedTokens)
			skippedTokens->push(fTokens.front());
		return fTokens.front();
	}
 
	void ExpectToken(const string& expectedToken)
	{
		string token = GetCurrentToken();
		if (expectedToken != token) {
			throw ParseException(string("Unexpected token `") + token
				+ "'. Expected was `" + expectedToken + "'.");
		}
	}
 
	void ExpectNextToken(const string& expectedToken)
	{
		GetNextToken();
		ExpectToken(expectedToken);
	}
 
	bool CheckToken(const string& expectedToken)
	{
		string token = GetCurrentToken();
		return (expectedToken == token);
	}
 
	bool CheckNextToken(const string& expectedToken)
	{
		GetNextToken();
		bool result = CheckToken(expectedToken);
		if (!result)
			PutCurrentToken();
		return result;
	}
 
	void PutCurrentToken()
	{
		if (!fHasCurrent)
			throw Exception("GetCurrentToken(): No current token!");
		fHasCurrent = false;
	}
 
	void PutToken(string token)
	{
		if (fHasCurrent) {
			fTokens.pop_front();
			fHasCurrent = false;
		}
		fTokens.push_front(token);
	}
 
	void PutTokens(stack<string>& tokens)
	{
		if (fHasCurrent) {
			fTokens.pop_front();
			fHasCurrent = false;
		}
		while (!tokens.empty()) {
			fTokens.push_front(tokens.top());
			tokens.pop();
		}
	}
 
private:
	void _ReadLine()
	{
		// read the line
		char buffer[10240];
		if (!fInput.getline(buffer, sizeof(buffer)))
			throw EOFException("Unexpected end of input.");
		// parse it
		vector<string> line;
		int len = strlen(buffer);
		int tokenStart = 0;
		for (int i = 0; i < len; i++) {
			char c = buffer[i];
			if (isspace(c)) {
				if (tokenStart < i)
					line.push_back(string(buffer + tokenStart, buffer + i));
				tokenStart = i + 1;
				continue;
			}
			switch (buffer[i]) {
				case '#':
				case '(':
				case ')':
				case '*':
				case '&':
				case '[':
				case ']':
				case ';':
				case ',':
					if (tokenStart < i) {
						line.push_back(string(buffer + tokenStart,
												 buffer + i));
					}
					line.push_back(string(buffer + i, buffer + i + 1));
					tokenStart = i + 1;
					break;
			}
		}
		if (tokenStart < len)
			line.push_back(string(buffer + tokenStart, buffer + len));
		// drop the line, if it starts with "# <number>", as those are
		// directions from the pre-processor to the compiler
		if (line.size() >= 2) {
			if (line[0] == "#" && isdigit(line[1][0]))
				return;
		}
		for (int i = 0; i < (int)line.size(); i++)
			fTokens.push_back(line[i]);
	}
 
private:
	istream&		fInput;
	list<string>	fTokens;
	bool			fHasCurrent;
};
 
 
class Main {
public:
 
	int Run(int argc, char** argv)
	{
		// parse parameters
		if (argc >= 2
			&& (string(argv[1]) == "-h" || string(argv[1]) == "--help")) {
			print_usage(false);
			return 0;
		}
		if (argc != 4) {
			print_usage(true);
			return 1;
		}
		_ParseSyscalls(argv[1]);
		_WriteSyscallInfoFile(argv[2]);
		_WriteSyscallTypeSizes(argv[3]);
		return 0;
	}
 
private:
	void _ParseSyscalls(const char* filename)
	{
		// open the input file
		ifstream file(filename, ifstream::in);
		if (!file.is_open())
			throw new IOException(string("Failed to open `") + filename + "'.");
		// parse the syscalls
		Tokenizer tokenizer(file);
		// find "#pragma syscalls begin"
		while (true) {
			while (tokenizer.GetNextToken() != "#");
			stack<string> skippedTokens;
			if (tokenizer.GetNextToken(skippedTokens) == "pragma"
				&& tokenizer.GetNextToken(skippedTokens) == "syscalls"
				&& tokenizer.GetNextToken(skippedTokens) == "begin") {
				break;
			}
			tokenizer.PutTokens(skippedTokens);
		}
		// parse the functions
		while (!tokenizer.CheckNextToken("#")) {
			Syscall syscall;
			_ParseSyscall(tokenizer, syscall);
			fSyscalls.push_back(syscall);
		}
		// expect "pragma syscalls end"
		tokenizer.ExpectNextToken("pragma");
		tokenizer.ExpectNextToken("syscalls");
		tokenizer.ExpectNextToken("end");
	}
 
	void _WriteSyscallInfoFile(const char* filename)
	{
		// open the syscall info file
		ofstream file(filename, ofstream::out | ofstream::trunc);
		if (!file.is_open())
			throw new IOException(string("Failed to open `") + filename + "'.");
 
		// write preamble
		file << "#include \"gensyscalls.h\"" << endl;
		file << "#include \"syscall_types_sizes.h\"" << endl;
		file << endl;
 
		file << "const char* const kReturnTypeAlignmentType = \""
			EVAL_MACRO(MAKE_STRING, SYSCALL_RETURN_TYPE_ALIGNMENT_TYPE)
			<< "\";" << endl;
		file << "const char* const kParameterAlignmentType = \""
			EVAL_MACRO(MAKE_STRING, SYSCALL_PARAMETER_ALIGNMENT_TYPE)
			<< "\";" << endl;
		file << "const int kReturnTypeAlignmentSize = "
			"SYSCALL_RETURN_TYPE_ALIGNMENT_SIZE;" << endl;
		file << "const int kParameterAlignmentSize = "
			"SYSCALL_PARAMETER_ALIGNMENT_SIZE;" << endl;
		file << endl;
 
		file << "SyscallVector* create_syscall_vector() {" << endl;
		file << "\tSyscallVector* syscallVector = SyscallVector::Create();"
			<< endl;
		file << "\tSyscall* syscall;" << endl;
 
		// syscalls
		for (int i = 0; i < (int)fSyscalls.size(); i++) {
			const Syscall& syscall = fSyscalls[i];
 
			// syscall = syscallVector->CreateSyscall("syscallName",
			//	"syscallKernelName");
			file << "\tsyscall = syscallVector->CreateSyscall(\""
				<< syscall.GetName() << "\", \""
				<< syscall.GetKernelName() << "\");" << endl;
 
			const Type& returnType = syscall.GetReturnType();
 
			// syscall->SetReturnType<(SYSCALL_RETURN_TYPE_SIZE_<i>,
			//		"returnType");
			file << "\tsyscall->SetReturnType("
				<< "SYSCALL_RETURN_TYPE_SIZE_" << i << ", \""
				<< returnType.type << "\");" << endl;
 
			// parameters
			int paramCount = syscall.CountParameters();
			for (int k = 0; k < paramCount; k++) {
				const NamedType& param = syscall.ParameterAt(k);
				// syscall->AddParameter(SYSCALL_PARAMETER_SIZE_<i>_<k>,
				//		"parameterTypeName", "parameterName");
				file << "\tsyscall->AddParameter("
					<< "SYSCALL_PARAMETER_SIZE_" << i << "_" << k
					<< ", \"" << param.type << "\", \"" << param.name << "\");"
					<< endl;
			}
			file << endl;
		}
 
		// postamble
		file << "\treturn syscallVector;" << endl;
		file << "}" << endl;
	}
 
	void _WriteSyscallTypeSizes(const char* filename)
	{
		// open the syscall info file
		ofstream file(filename, ofstream::out | ofstream::trunc);
		if (!file.is_open())
			throw new IOException(string("Failed to open `") + filename + "'.");
 
		// write preamble
		file << "#include <computed_asm_macros.h>" << endl;
		file << "#include <syscalls.h>" << endl;
		file << endl;
		file << "#include \"arch_gensyscalls.h\"" << endl;
		file << endl;
		file << "void dummy() {" << endl;
 
		file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_ALIGNMENT_SIZE, "
			"sizeof(SYSCALL_RETURN_TYPE_ALIGNMENT_TYPE));" << endl;
  		file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_PARAMETER_ALIGNMENT_SIZE, "
			"sizeof(SYSCALL_PARAMETER_ALIGNMENT_TYPE));" << endl;
		file << endl;
 
		// syscalls
		for (int i = 0; i < (int)fSyscalls.size(); i++) {
			const Syscall& syscall = fSyscalls[i];
			const Type& returnType = syscall.GetReturnType();
 
			if (returnType.type == "void") {
				file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_SIZE_"
					<< i << ", 0);" << endl;
			} else {
				file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_SIZE_"
					<< i << ", sizeof(" << returnType.type << "));" << endl;
			}
 
			// parameters
			int paramCount = syscall.CountParameters();
			for (int k = 0; k < paramCount; k++) {
				const NamedType& param = syscall.ParameterAt(k);
				file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_PARAMETER_SIZE_" << i
					<< "_" << k << ", sizeof(" << param.type << "));" << endl;
			}
			file << endl;
		}
 
		// postamble
		file << "}" << endl;
	}
 
	void _ParseSyscall(Tokenizer& tokenizer, Syscall& syscall)
	{
		// get return type and function name
		vector<string> returnType;
		while (tokenizer.GetNextToken() != "(") {
			string token = tokenizer.GetCurrentToken();
			// strip leading "extern"
			if (!returnType.empty() || token != "extern")
				returnType.push_back(token);
		}
		if (returnType.size() < 2) {
			throw ParseException("Error while parsing function "
				"return type.");
		}
		syscall.SetName(returnType[returnType.size() - 1]);
		returnType.pop_back();
		string returnTypeString(returnType[0]);
		for (int i = 1; i < (int)returnType.size(); i++) {
			returnTypeString += " ";
			returnTypeString += returnType[i];
		}
		syscall.SetReturnType(returnTypeString);
		// get arguments
		if (!tokenizer.CheckNextToken(")")) {
			_ParseParameter(tokenizer, syscall);
			while (!tokenizer.CheckNextToken(")")) {
				tokenizer.ExpectNextToken(",");
				_ParseParameter(tokenizer, syscall);
			}
		}
		tokenizer.ExpectNextToken(";");
	}
 
	void _ParseParameter(Tokenizer& tokenizer, Syscall& syscall)
	{
		vector<string> type;
		while (tokenizer.GetNextToken() != ")"
			&& tokenizer.GetCurrentToken() != ",") {
			string token = tokenizer.GetCurrentToken();
			type.push_back(token);
			if (token == "(") {
				// This must be a function pointer. We deal with that in a
				// separate method.
				_ParseFunctionPointerParameter(tokenizer, syscall, type);
				return;
			}
		}
		tokenizer.PutCurrentToken();
		if (type.size() < 2) {
			if (type.size() == 1 && type[0] == "void") {
				// that's OK
				return;
			}
			throw ParseException("Error while parsing function parameter.");
		}
 
		// last component is the parameter name
		string typeName = type.back();
		type.pop_back();
 
		string typeString(type[0]);
		for (int i = 1; i < (int)type.size(); i++) {
			typeString += " ";
			typeString += type[i];
		}
		syscall.AddParameter(NamedType(typeString, typeName));
	}
 
	void _ParseFunctionPointerParameter(Tokenizer& tokenizer, Syscall& syscall,
		vector<string>& type)
	{
		// When this method is entered, the return type and the opening
		// parenthesis must already be parse and stored in the supplied type
		// vector.
		if (type.size() < 2) {
			throw ParseException("Error parsing function parameter. "
				"No return type.");
		}
		// read all the "*"s there are
		while (tokenizer.CheckNextToken("*"))
			type.push_back("*");
		// now comes the parameter name, if specified -- skip it
		string typeName;
		if (!tokenizer.CheckNextToken(")")) {
			typeName = tokenizer.GetNextToken();
			tokenizer.ExpectNextToken(")");
		} else {
			throw ParseException("Error while parsing function parameter. "
				"No parameter name.");
		}
		type.push_back(")");
		// the function parameters
		tokenizer.ExpectNextToken("(");
		type.push_back("(");
		while (!tokenizer.CheckNextToken(")"))
			type.push_back(tokenizer.GetNextToken());
		type.push_back(")");
		// compose the type string and add it to the syscall parameters
		string typeString(type[0]);
		for (int i = 1; i < (int)type.size(); i++) {
			typeString += " ";
			typeString += type[i];
		}
		syscall.AddParameter(NamedType(typeString, typeName));
	}
 
private:
	vector<Syscall>	fSyscalls;
};
 
 
int
main(int argc, char** argv)
{
	try {
		return Main().Run(argc, argv);
	} catch (Exception& exception) {
		fprintf(stderr, "%s\n", exception.what());
		return 1;
	}
}

V1022 An exception was thrown by pointer. Consider throwing it by value instead.

V1022 An exception was thrown by pointer. Consider throwing it by value instead.

V1022 An exception was thrown by pointer. Consider throwing it by value instead.