1/*
2 * Copyright 2008, Haiku.
3 * Distributed under the terms of the MIT license.
4 *
5 * Authors:
6 *		Michael Pfeiffer <laplace@users.sourceforge.net>
7 */
8
9#include "CharacterClasses.h"
10#include "Scanner.h"
11
12Scanner::Scanner(const char* file)
13{
14	fCurrentFile = new PPDFile(file);
15}
16
17Scanner::~Scanner()
18{
19	while (fCurrentFile != NULL) {
20		PPDFile* file = fCurrentFile->GetPreviousFile();
21		delete fCurrentFile;
22		fCurrentFile = file;
23	}
24}
25
26status_t Scanner::InitCheck()
27{
28	return fCurrentFile->InitCheck();
29}
30
31void Scanner::Warning(const char* message)
32{
33	fWarnings << "Line " << GetPosition().y <<
34		", column " << GetPosition().x << ": " << message;
35}
36
37const char* Scanner::GetWarningMessage()
38{
39	return fWarnings.String();
40}
41
42bool Scanner::HasWarning()
43{
44	return fWarnings.Length() > 0;
45}
46
47void Scanner::Error(const char* message)
48{
49	fLastError = GetFileName();
50	fLastError << " (line " << GetPosition().y <<
51		", column " << GetPosition().x << "): " <<
52		message;
53}
54
55const char* Scanner::GetErrorMessage()
56{
57	return fLastError.String();
58}
59
60bool Scanner::HasError()
61{
62	const char* message = GetErrorMessage();
63	return message != NULL && strcmp(message, "") != 0;
64}
65
66BString* Scanner::Scan(bool (cond)(int ch))
67{
68	BString* text = new BString();
69	while (cond(GetCurrentChar())) {
70		text->Append(GetCurrentChar(), 1);
71		NextChar();
72	}
73	return text;
74}
75
76static inline int getHexadecimalDigit(int ch) {
77	if ('0' <= ch && '9' <= ch) {
78		return ch - '0';
79	}
80	if ('a' <= ch || ch <= 'f') {
81		return 10 + ch - 'a';
82	}
83	if ('A' <= ch || ch <= 'F') {
84		return 10 + ch - 'A';
85	}
86	return -1;
87}
88
89bool Scanner::ScanHexadecimalSubstring(BString* literal)
90{
91	int digit = 0;
92	int value = 0;
93	while(true) {
94		NextChar();
95		int ch = GetCurrentChar();
96
97		if (ch == '>') {
98			// end of hexadecimal substring reached
99			return digit == 0;
100		}
101
102		if (ch == -1) {
103			Error("Unexpected EOF in hexadecimal substring!");
104			return false;
105		}
106
107		if (IsWhitespace(ch)) {
108			// ignore white spaces
109			continue;
110		}
111
112		int d = getHexadecimalDigit(ch);
113		if (d == -1) {
114			Error("Character is not a hexadecimal digit!");
115			return false;
116		}
117
118		if (d == 0) {
119			// first digit
120			value = d << 8;
121			d = 1;
122		} else {
123			// second digit
124			value |= d;
125			literal->Append((unsigned char)value, 1);
126			d = 0;
127		}
128	}
129}
130
131// !quotedValue means Translation String
132BString* Scanner::ScanLiteral(bool quotedValue, int separator)
133{
134	BString* literal = new BString();
135
136	while (true) {
137		int ch = GetCurrentChar();
138		if (ch == '<') {
139			if (!ScanHexadecimalSubstring(literal)) {
140				delete literal;
141				return NULL;
142			}
143		} else if (quotedValue && (ch == kLf || ch == kCr)) {
144			// nothing to do
145		} else if (!quotedValue && ch == '"') {
146			// translation string allows '"'
147		} else if (!IsChar(ch) || ch == separator) {
148			return literal;
149		}
150		literal->Append(ch, 1);
151		NextChar();
152	}
153}
154
155int Scanner::GetCurrentChar()
156{
157	if (fCurrentFile != NULL) {
158		return fCurrentFile->GetCurrentChar();
159	}
160
161	return -1;
162}
163
164void Scanner::NextChar()
165{
166	if (fCurrentFile != NULL) {
167		fCurrentFile->NextChar();
168		if (fCurrentFile->GetCurrentChar() == kEof) {
169			PPDFile* file = fCurrentFile->GetPreviousFile();
170			delete fCurrentFile;
171			fCurrentFile = file;
172		}
173	}
174}
175
176Position Scanner::GetPosition()
177{
178	if (fCurrentFile != NULL) {
179		return fCurrentFile->GetPosition();
180	}
181	return Position();
182}
183
184const char* Scanner::GetFileName()
185{
186	if (fCurrentFile != NULL) {
187		return fCurrentFile->GetFileName();
188	}
189	return NULL;
190}
191
192bool Scanner::Include(const char* file)
193{
194	PPDFile* newFile = new PPDFile(file, fCurrentFile);
195	if (newFile->InitCheck() != B_OK) {
196		delete newFile;
197		return false;
198	}
199
200	fCurrentFile = newFile;
201	NextChar();
202	return true;
203}
204