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