xref: /haiku/src/tools/gensyscalls/gensyscallinfos.cpp (revision 0dfceb2b8af9cd27312407bf35879ae38980a664)
1 /*
2  * Copyright 2004-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <cstdio>
8 #include <cstdlib>
9 #include <fstream>
10 #include <list>
11 #include <stack>
12 #include <string>
13 #include <string.h>
14 #include <vector>
15 
16 #include "gensyscalls_common.h"
17 
18 
19 const char* kUsage =
20 	"Usage: gensyscallinfos <header> <syscall infos>\n"
21 	"\n"
22 	"Given the (preprocessed) header file that defines the syscall prototypes "
23 		"the\n"
24 	"command generates a source file consisting of syscall infos, which is "
25 		"needed\n"
26 	"to build gensyscalls, which in turn generates the assembly syscall\n"
27 	"definitions and code for the kernelland syscall dispatcher.\n"
28 	"\n"
29 	"  <header>               - Input: The preprocessed header file with the\n"
30 	"                           syscall prototypes.\n"
31 	"  <syscall infos>        - Output: The syscall infos source file needed "
32 		"to\n"
33 	"                           build gensyscalls.";
34 
35 
36 static void
37 print_usage(bool error)
38 {
39 	fprintf((error ? stderr : stdout), kUsage);
40 }
41 
42 
43 struct Type {
44 	Type(const char* type) : type(type) {}
45 	Type(const string& type) : type(type) {}
46 
47 	string type;
48 };
49 
50 
51 struct NamedType : Type {
52 	NamedType(const char* type, const char* name)
53 		: Type(type), name(name) {}
54 	NamedType(const string& type, const string& name)
55 		: Type(type), name(name) {}
56 
57 	string name;
58 };
59 
60 
61 class Function {
62 public:
63 	Function() : fReturnType("") {}
64 
65 	void SetName(const string& name)
66 	{
67 		fName = name;
68 	}
69 
70 	const string& GetName() const
71 	{
72 		return fName;
73 	}
74 
75 	void AddParameter(const NamedType& type)
76 	{
77 		fParameters.push_back(type);
78 	}
79 
80 	int CountParameters() const
81 	{
82 		return fParameters.size();
83 	}
84 
85 	const NamedType& ParameterAt(int index) const
86 	{
87 		return fParameters[index];
88 	}
89 
90 	void SetReturnType(const Type& type)
91 	{
92 		fReturnType = type;
93 	}
94 
95 	const Type& GetReturnType() const
96 	{
97 		return fReturnType;
98 	}
99 
100 protected:
101 	string				fName;
102 	vector<NamedType>	fParameters;
103 	Type				fReturnType;
104 };
105 
106 
107 class Syscall : public Function {
108 public:
109 	string GetKernelName() const
110 	{
111 		int baseIndex = 0;
112 		if (fName.find("_kern_") == 0)
113 			baseIndex = strlen("_kern_");
114 		else if (fName.find("sys_") == 0)
115 			baseIndex = strlen("sys_");
116 		string kernelName("_user_");
117 		kernelName.append(string(fName, baseIndex));
118 		return kernelName;
119 	}
120 };
121 
122 
123 class Tokenizer {
124 public:
125 	Tokenizer(istream& input)
126 		: fInput(input),
127 		  fHasCurrent(false)
128 	{
129 	}
130 
131 	string GetCurrentToken()
132 	{
133 		if (!fHasCurrent)
134 			throw Exception("GetCurrentToken(): No current token!");
135 		return fTokens.front();
136 	}
137 
138 	string GetNextToken()
139 	{
140 		return GetNextToken(NULL);
141 	}
142 
143 	string GetNextToken(stack<string>& skippedTokens)
144 	{
145 		return GetNextToken(&skippedTokens);
146 	}
147 
148 	string GetNextToken(stack<string>* skippedTokens)
149 	{
150 		if (fHasCurrent) {
151 			fTokens.pop_front();
152 			fHasCurrent = false;
153 		}
154 		while (fTokens.empty())
155 			_ReadLine();
156 		fHasCurrent = true;
157 		if (skippedTokens)
158 			skippedTokens->push(fTokens.front());
159 		return fTokens.front();
160 	}
161 
162 	void ExpectToken(const string& expectedToken)
163 	{
164 		string token = GetCurrentToken();
165 		if (expectedToken != token) {
166 			throw ParseException(string("Unexpected token `") + token
167 				+ "'. Expected was `" + expectedToken + "'.");
168 		}
169 	}
170 
171 	void ExpectNextToken(const string& expectedToken)
172 	{
173 		GetNextToken();
174 		ExpectToken(expectedToken);
175 	}
176 
177 	bool CheckToken(const string& expectedToken)
178 	{
179 		string token = GetCurrentToken();
180 		return (expectedToken == token);
181 	}
182 
183 	bool CheckNextToken(const string& expectedToken)
184 	{
185 		GetNextToken();
186 		bool result = CheckToken(expectedToken);
187 		if (!result)
188 			PutCurrentToken();
189 		return result;
190 	}
191 
192 	void PutCurrentToken()
193 	{
194 		if (!fHasCurrent)
195 			throw Exception("GetCurrentToken(): No current token!");
196 		fHasCurrent = false;
197 	}
198 
199 	void PutToken(string token)
200 	{
201 		if (fHasCurrent) {
202 			fTokens.pop_front();
203 			fHasCurrent = false;
204 		}
205 		fTokens.push_front(token);
206 	}
207 
208 	void PutTokens(stack<string>& tokens)
209 	{
210 		if (fHasCurrent) {
211 			fTokens.pop_front();
212 			fHasCurrent = false;
213 		}
214 		while (!tokens.empty()) {
215 			fTokens.push_front(tokens.top());
216 			tokens.pop();
217 		}
218 	}
219 
220 private:
221 	void _ReadLine()
222 	{
223 		// read the line
224 		char buffer[10240];
225 		if (!fInput.getline(buffer, sizeof(buffer)))
226 			throw EOFException("Unexpected end of input.");
227 		// parse it
228 		vector<string> line;
229 		int len = strlen(buffer);
230 		int tokenStart = 0;
231 		for (int i = 0; i < len; i++) {
232 			char c = buffer[i];
233 			if (isspace(c)) {
234 				if (tokenStart < i)
235 					line.push_back(string(buffer + tokenStart, buffer + i));
236 				tokenStart = i + 1;
237 				continue;
238 			}
239 			switch (buffer[i]) {
240 				case '#':
241 				case '(':
242 				case ')':
243 				case '*':
244 				case '&':
245 				case '[':
246 				case ']':
247 				case ';':
248 				case ',':
249 					if (tokenStart < i) {
250 						line.push_back(string(buffer + tokenStart,
251 												 buffer + i));
252 					}
253 					line.push_back(string(buffer + i, buffer + i + 1));
254 					tokenStart = i + 1;
255 					break;
256 			}
257 		}
258 		if (tokenStart < len)
259 			line.push_back(string(buffer + tokenStart, buffer + len));
260 		// drop the line, if it starts with "# <number>", as those are
261 		// directions from the pre-processor to the compiler
262 		if (line.size() >= 2) {
263 			if (line[0] == "#" && isdigit(line[1][0]))
264 				return;
265 		}
266 		for (int i = 0; i < (int)line.size(); i++)
267 			fTokens.push_back(line[i]);
268 	}
269 
270 private:
271 	istream&		fInput;
272 	list<string>	fTokens;
273 	bool			fHasCurrent;
274 };
275 
276 
277 class Main {
278 public:
279 
280 	int Run(int argc, char** argv)
281 	{
282 		// parse parameters
283 		if (argc >= 2
284 			&& (string(argv[1]) == "-h" || string(argv[1]) == "--help")) {
285 			print_usage(false);
286 			return 0;
287 		}
288 		if (argc != 3) {
289 			print_usage(true);
290 			return 1;
291 		}
292 		_ParseSyscalls(argv[1]);
293 		_WriteSyscallInfoFile(argv[2]);
294 		return 0;
295 	}
296 
297 private:
298 	void _ParseSyscalls(const char* filename)
299 	{
300 		// open the input file
301 		ifstream file(filename, ifstream::in);
302 		if (!file.is_open())
303 			throw new IOException(string("Failed to open `") + filename + "'.");
304 		// parse the syscalls
305 		Tokenizer tokenizer(file);
306 		// find "#pragma syscalls begin"
307 		while (true) {
308 			while (tokenizer.GetNextToken() != "#");
309 			stack<string> skippedTokens;
310 			if (tokenizer.GetNextToken(skippedTokens) == "pragma"
311 				&& tokenizer.GetNextToken(skippedTokens) == "syscalls"
312 				&& tokenizer.GetNextToken(skippedTokens) == "begin") {
313 				break;
314 			}
315 			tokenizer.PutTokens(skippedTokens);
316 		}
317 		// parse the functions
318 		while (!tokenizer.CheckNextToken("#")) {
319 			Syscall syscall;
320 			_ParseSyscall(tokenizer, syscall);
321 			fSyscalls.push_back(syscall);
322 		}
323 		// expect "pragma syscalls end"
324 		tokenizer.ExpectNextToken("pragma");
325 		tokenizer.ExpectNextToken("syscalls");
326 		tokenizer.ExpectNextToken("end");
327 	}
328 
329 	void _WriteSyscallInfoFile(const char* filename)
330 	{
331 		// open the syscall info file
332 		ofstream file(filename, ofstream::out | ofstream::trunc);
333 		if (!file.is_open())
334 			throw new IOException(string("Failed to open `") + filename + "'.");
335 
336 		// write preamble
337 		file << "#include \"gensyscalls.h\"" << endl;
338 		file << "#include \"syscalls.h.pp\"" << endl;
339 		file << endl;
340 
341 		file << "SyscallVector* create_syscall_vector() {" << endl;
342 		file << "\tSyscallVector* syscallVector = SyscallVector::Create();"
343 			<< endl;
344 		file << "\tSyscall* syscall;" << endl;
345 
346 		// syscalls
347 		for (int i = 0; i < (int)fSyscalls.size(); i++) {
348 			const Syscall& syscall = fSyscalls[i];
349 
350 			// syscall = syscallVector->CreateSyscall("syscallName",
351 			//	"syscallKernelName");
352 			file << "\tsyscall = syscallVector->CreateSyscall(\""
353 				<< syscall.GetName() << "\", \""
354 				<< syscall.GetKernelName() << "\");" << endl;
355 
356 			const Type& returnType = syscall.GetReturnType();
357 
358 			// syscall->SetReturnType<ReturnType>("returnType");
359 			file << "\tsyscall->SetReturnType<" << returnType.type
360 				<< ">(\"" << returnType.type << "\");" << endl;
361 
362 			// parameters
363 			int paramCount = syscall.CountParameters();
364 			for (int k = 0; k < paramCount; k++) {
365 				const NamedType& param = syscall.ParameterAt(k);
366 				// syscall->AddParameter<ParameterType>("parameterTypeName",
367 				//	"parameterName");
368 				file << "\tsyscall->AddParameter<"
369 					<< param.type << ">(\""
370 					<< param.type << "\", \""
371 					<< param.name << "\");" << endl;
372 			}
373 			file << endl;
374 		}
375 
376 		// postamble
377 		file << "\treturn syscallVector;" << endl;
378 		file << "}" << endl;
379 	}
380 
381 	void _ParseSyscall(Tokenizer& tokenizer, Syscall& syscall)
382 	{
383 		// get return type and function name
384 		vector<string> returnType;
385 		while (tokenizer.GetNextToken() != "(") {
386 			string token = tokenizer.GetCurrentToken();
387 			// strip leading "extern"
388 			if (!returnType.empty() || token != "extern")
389 				returnType.push_back(token);
390 		}
391 		if (returnType.size() < 2) {
392 			throw ParseException("Error while parsing function "
393 				"return type.");
394 		}
395 		syscall.SetName(returnType[returnType.size() - 1]);
396 		returnType.pop_back();
397 		string returnTypeString(returnType[0]);
398 		for (int i = 1; i < (int)returnType.size(); i++) {
399 			returnTypeString += " ";
400 			returnTypeString += returnType[i];
401 		}
402 		syscall.SetReturnType(returnTypeString);
403 		// get arguments
404 		if (!tokenizer.CheckNextToken(")")) {
405 			_ParseParameter(tokenizer, syscall);
406 			while (!tokenizer.CheckNextToken(")")) {
407 				tokenizer.ExpectNextToken(",");
408 				_ParseParameter(tokenizer, syscall);
409 			}
410 		}
411 		tokenizer.ExpectNextToken(";");
412 	}
413 
414 	void _ParseParameter(Tokenizer& tokenizer, Syscall& syscall)
415 	{
416 		vector<string> type;
417 		while (tokenizer.GetNextToken() != ")"
418 			&& tokenizer.GetCurrentToken() != ",") {
419 			string token = tokenizer.GetCurrentToken();
420 			type.push_back(token);
421 			if (token == "(") {
422 				// This must be a function pointer. We deal with that in a
423 				// separate method.
424 				_ParseFunctionPointerParameter(tokenizer, syscall, type);
425 				return;
426 			}
427 		}
428 		tokenizer.PutCurrentToken();
429 		if (type.size() < 2) {
430 			if (type.size() == 1 && type[0] == "void") {
431 				// that's OK
432 				return;
433 			}
434 			throw ParseException("Error while parsing function parameter.");
435 		}
436 
437 		// last component is the parameter name
438 		string typeName = type.back();
439 		type.pop_back();
440 
441 		string typeString(type[0]);
442 		for (int i = 1; i < (int)type.size(); i++) {
443 			typeString += " ";
444 			typeString += type[i];
445 		}
446 		syscall.AddParameter(NamedType(typeString, typeName));
447 	}
448 
449 	void _ParseFunctionPointerParameter(Tokenizer& tokenizer, Syscall& syscall,
450 		vector<string>& type)
451 	{
452 		// When this method is entered, the return type and the opening
453 		// parenthesis must already be parse and stored in the supplied type
454 		// vector.
455 		if (type.size() < 2) {
456 			throw ParseException("Error parsing function parameter. "
457 				"No return type.");
458 		}
459 		// read all the "*"s there are
460 		while (tokenizer.CheckNextToken("*"))
461 			type.push_back("*");
462 		// now comes the parameter name, if specified -- skip it
463 		string typeName;
464 		if (!tokenizer.CheckNextToken(")")) {
465 			typeName = tokenizer.GetNextToken();
466 			tokenizer.ExpectNextToken(")");
467 		} else {
468 			throw ParseException("Error while parsing function parameter. "
469 				"No parameter name.");
470 		}
471 		type.push_back(")");
472 		// the function parameters
473 		tokenizer.ExpectNextToken("(");
474 		type.push_back("(");
475 		while (!tokenizer.CheckNextToken(")"))
476 			type.push_back(tokenizer.GetNextToken());
477 		type.push_back(")");
478 		// compose the type string and add it to the syscall parameters
479 		string typeString(type[0]);
480 		for (int i = 1; i < (int)type.size(); i++) {
481 			typeString += " ";
482 			typeString += type[i];
483 		}
484 		syscall.AddParameter(NamedType(typeString, typeName));
485 	}
486 
487 private:
488 	vector<Syscall>	fSyscalls;
489 };
490 
491 
492 int
493 main(int argc, char** argv)
494 {
495 	try {
496 		return Main().Run(argc, argv);
497 	} catch (Exception& exception) {
498 		fprintf(stderr, "%s\n", exception.what());
499 		return 1;
500 	}
501 }
502