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