xref: /haiku/src/tests/add-ons/print/ppd/parser/PPDParser.cpp (revision 4420c1ceffd7f2246e4303d59cf02ed122980e9d)
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 "PPDParser.h"
10 
11 #include "AutoDelete.h"
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 
16 // #define VERBOSE 1
17 
18 struct Keyword {
19 	const char* name;
20 	const char* since;
21 	int major;
22 	int minor;
23 	bool found;
24 };
25 
26 static const Keyword gRequiredKeywords[] = {
27 	{"DefaultImageableArea", NULL},
28 	{"DefaultPageRegion", NULL},
29 	{"DefaultPageSize", NULL},
30 	{"DefaultPaperDimension", NULL},
31 	// sometimes missing
32 	// {"FileVersion", NULL},
33 	//
34 	// {"FormatVersion", NULL},
35 	{"ImageableArea", NULL},
36 	// "since" is not specified in standard!
37 	{"LanguageEncoding", "4.3"},
38 	{"LanguageVersion", NULL},
39 	{"Manufacturer", "4.3"},
40 	{"ModelName", NULL},
41 	{"NickName", NULL},
42 	{"PageRegion", NULL},
43 	{"PageSize", NULL},
44 	{"PaperDimension", NULL},
45 	{"PCFileName", NULL},
46 	{"PPD-Adobe", NULL},
47 	{"Product", NULL},
48 	{"PSVersion", NULL},
49 	// sometimes missing
50 	// {"ShortNickName", "4.3"},
51 };
52 
53 // e.g. *PPD.Adobe: "4.3"
54 const char* kPPDAdobe = "PPD-Adobe";
55 
56 #define NUMBER_OF_REQUIRED_KEYWORDS (int)(sizeof(gRequiredKeywords) / sizeof(struct Keyword))
57 
58 class RequiredKeywords
59 {
60 private:
61 	Keyword fKeywords[NUMBER_OF_REQUIRED_KEYWORDS];
62 	bool    fGotVersion;
63 	int     fMajorVersion;
64 	int     fMinorVersion;
65 
66 	void ExtractVersion(int& major, int& minor, const char* version);
67 	bool IsVersionRequired(Keyword* keyword);
68 
69 public:
70 	RequiredKeywords();
71 	bool IsRequired(Statement* statement);
72 	bool IsComplete();
73 	void AppendMissing(BString* string);
74 };
75 
RequiredKeywords()76 RequiredKeywords::RequiredKeywords()
77 	: fGotVersion(false)
78 {
79 	for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
80 		fKeywords[i] = gRequiredKeywords[i];
81 		fKeywords[i].found = false;
82 		const char* since = fKeywords[i].since;
83 		if (since != NULL) {
84 			ExtractVersion(fKeywords[i].major, fKeywords[i].minor, since);
85 		}
86 	}
87 }
88 
ExtractVersion(int & major,int & minor,const char * version)89 void RequiredKeywords::ExtractVersion(int& major, int& minor, const char* version)
90 {
91 	major = atoi(version);
92 	minor = 0;
93 	version = strchr(version, '.');
94 	if (version != NULL) {
95 		version ++;
96 		minor = atoi(version);
97 	}
98 }
99 
IsVersionRequired(Keyword * keyword)100 bool RequiredKeywords::IsVersionRequired(Keyword* keyword)
101 {
102 	if (keyword->since == NULL) return true;
103 	// be conservative if version is missing
104 	if (!fGotVersion) return true;
105 	// keyword is not required if file version < since
106 	if (fMajorVersion < keyword->major) return false;
107 	if (fMajorVersion == keyword->major &&
108 		fMinorVersion < keyword->minor) return false;
109 	return true;
110 }
111 
IsRequired(Statement * statement)112 bool RequiredKeywords::IsRequired(Statement* statement)
113 {
114 	const char* keyword = statement->GetKeyword()->String();
115 
116 	if (!fGotVersion && strcmp(kPPDAdobe, keyword) == 0 &&
117 		statement->GetValue() != NULL) {
118 		Value* value = statement->GetValue();
119 		BString* string = value->GetValue();
120 		fGotVersion = true;
121 		ExtractVersion(fMajorVersion, fMinorVersion, string->String());
122 	}
123 
124 	BString defaultKeyword;
125 	if (statement->GetType() == Statement::kDefault) {
126 		defaultKeyword << "Default" << keyword;
127 		keyword = defaultKeyword.String();
128 	}
129 
130 	for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
131 		const char* name = fKeywords[i].name;
132 		if (strcmp(name, keyword) == 0) {
133 			fKeywords[i].found = true;
134 			return true;
135 		}
136 	}
137 
138 	return false;
139 }
140 
IsComplete()141 bool RequiredKeywords::IsComplete()
142 {
143 	for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
144 		if (!fKeywords[i].found && IsVersionRequired(&fKeywords[i])) {
145 			return false;
146 		}
147 	}
148 	return true;
149 }
150 
AppendMissing(BString * string)151 void RequiredKeywords::AppendMissing(BString* string)
152 {
153 	for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
154 		if (!fKeywords[i].found && IsVersionRequired(&fKeywords[i])) {
155 			*string << "Keyword " << fKeywords[i].name;
156 			if (fKeywords[i].since != NULL) {
157 				*string << fKeywords[i].major << ". " << fKeywords[i].minor
158 				<< " < " <<
159 				fMajorVersion << "." << fMinorVersion << " ";
160 			}
161 			*string << " is missing\n";
162 		}
163 	}
164 }
165 
166 // Constants
167 
168 static const char* kEndStatement = "End";
169 
170 // Implementation
171 
PPDParser(const char * file)172 PPDParser::PPDParser(const char* file)
173 	: Parser(file)
174 	, fStack(false)
175 	, fRequiredKeywords(new RequiredKeywords)
176 {
177 }
178 
~PPDParser()179 PPDParser::~PPDParser()
180 {
181 	delete fRequiredKeywords;
182 }
183 
Push(Statement * statement)184 void PPDParser::Push(Statement* statement)
185 {
186 	fStack.Add(statement);
187 }
188 
Top()189 Statement* PPDParser::Top()
190 {
191 	if (fStack.Size() > 0) {
192 		return fStack.StatementAt(fStack.Size()-1);
193 	}
194 	return NULL;
195 }
196 
Pop()197 void PPDParser::Pop()
198 {
199 	fStack.Remove(Top());
200 }
201 
AddStatement(Statement * statement)202 void PPDParser::AddStatement(Statement* statement)
203 {
204 	fRequiredKeywords->IsRequired(statement);
205 
206 	Statement* top = Top();
207 	if (top != NULL) {
208 		top->AddChild(statement);
209 	} else {
210 		fPPD->Add(statement);
211 	}
212 }
213 
IsValidOpenStatement(GroupStatement * statement)214 bool PPDParser::IsValidOpenStatement(GroupStatement* statement)
215 {
216 	if (statement->GetGroupName() == NULL) {
217 		Error("Missing group ID in open statement");
218 		return false;
219 	}
220 	return true;
221 }
222 
IsValidCloseStatement(GroupStatement * statement)223 bool PPDParser::IsValidCloseStatement(GroupStatement* statement)
224 {
225 	if (statement->GetGroupName() == NULL) {
226 		Error("Missing option in close statement");
227 		return false;
228 	}
229 
230 	if (Top() == NULL) {
231 		Error("Close statement without an open statement");
232 		return false;
233 	}
234 
235 	GroupStatement openStatement(Top());
236 
237 	// check if corresponding Open* is on top of stack
238 	BString open = openStatement.GetKeyword();
239 	open.RemoveFirst("Open");
240 	BString close = statement->GetKeyword();
241 	close.RemoveFirst("Close");
242 
243 	if (open != close) {
244 		Error("Close statement has no corresponding open statement");
245 #ifdef VERBOSE
246 		printf("********* OPEN ************\n");
247 		openStatement.GetStatement()->Print();
248 		printf("********* CLOSE ***********\n");
249 		statement->GetStatement()->Print();
250 #endif
251 		return false;
252 	}
253 
254 	BString openValue(openStatement.GetGroupName());
255 	BString closeValue(statement->GetGroupName());
256 
257 	const char* whiteSpaces = " \t";
258 	openValue.RemoveSet(whiteSpaces);
259 	closeValue.RemoveSet(whiteSpaces);
260 
261 	if (openValue != closeValue) {
262 		BString message("Open name does not match close name ");
263 		message << openValue << " != " << closeValue << "\n";
264 		Warning(message.String());
265 	}
266 
267 	return true;
268 }
269 
ParseStatement(Statement * _statement)270 bool PPDParser::ParseStatement(Statement* _statement)
271 {
272 	AutoDelete<Statement> statement(_statement);
273 
274 	if (_statement->GetKeyword() == NULL) {
275 		Error("Keyword missing");
276 		return false;
277 	}
278 
279 	if (_statement->GetOption() != NULL &&
280 		_statement->GetOption()->GetValue() == NULL) {
281 		// The parser should not provide an option without a value
282 		Error("Option has no value");
283 		return false;
284 	}
285 
286 	if (_statement->GetValue() != NULL &&
287 		_statement->GetValue()->GetValue() == NULL) {
288 		// The parser should not provide a value without a value
289 		Error("Value has no value");
290 		return false;
291 	}
292 
293 	const char* keyword = statement.Get()->GetKeyword()->String();
294 	if (strcmp(keyword, kEndStatement) == 0) {
295 		// End is ignored
296 		return true;
297 	}
298 
299 	GroupStatement group(statement.Get());
300 	if (group.IsOpenGroup()) {
301 
302 		if (!IsValidOpenStatement(&group)) {
303 			return false;
304 		}
305 		// Add() has to be infront of Push()!
306 		AddStatement(statement.Release());
307 		// begin of nested statement
308 		Push(statement.Get());
309 		return true;
310 	}
311 
312 	if (group.IsCloseGroup()) {
313 
314 		// end of nested statement
315 		if (!IsValidCloseStatement(&group)) {
316 			return false;
317 		}
318 
319 		Pop();
320 		// The closing statement is not stored
321 		return true;
322 	}
323 
324 	AddStatement(statement.Release());
325 	return true;
326 }
327 
ParseStatements()328 bool PPDParser::ParseStatements()
329 {
330 	Statement* statement;
331 	while ((statement = Parser::Parse()) != NULL) {
332 #ifdef VERBOSE
333 		statement->Print(); fflush(stdout);
334 #endif
335 		if (!ParseStatement(statement)) {
336 			return false;
337 		}
338 
339 		if (!fParseAll && fRequiredKeywords->IsComplete()) {
340 			break;
341 		}
342 	}
343 
344 	if (HasError()) {
345 		return false;
346 	}
347 
348 	if (Top() != NULL) {
349 		BString error("Missing close statement for:\n");
350 		do {
351 			error << " * " <<
352 				Top()->GetKeywordString() << " " <<
353 				Top()->GetOptionString() << "\n";
354 			Pop();
355 		} while (Top() != NULL);
356 		Error(error.String());
357 		return false;
358 	}
359 	return true;
360 }
361 
Parse(bool all)362 PPD* PPDParser::Parse(bool all)
363 {
364 	fParseAll = all;
365 
366 	if (InitCheck() != B_OK) return NULL;
367 
368 	fPPD = new PPD();
369 
370 	ParseStatements();
371 
372 	if (!HasError() && !fRequiredKeywords->IsComplete()) {
373 		BString string;
374 		fRequiredKeywords->AppendMissing(&string);
375 		Error(string.String());
376 	}
377 
378 	if (HasError()) {
379 		delete fPPD; fPPD = NULL;
380 		return NULL;
381 	}
382 
383 	return fPPD;
384 }
385 
ParseAll()386 PPD* PPDParser::ParseAll()
387 {
388 	return Parse(true);
389 }
390 
ParseHeader()391 PPD* PPDParser::ParseHeader()
392 {
393 	return Parse(false);
394 }
395 
396