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