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