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 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 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 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 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 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 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 172 PPDParser::PPDParser(const char* file) 173 : Parser(file) 174 , fStack(false) 175 , fRequiredKeywords(new RequiredKeywords) 176 { 177 } 178 179 PPDParser::~PPDParser() 180 { 181 delete fRequiredKeywords; 182 } 183 184 void PPDParser::Push(Statement* statement) 185 { 186 fStack.Add(statement); 187 } 188 189 Statement* PPDParser::Top() 190 { 191 if (fStack.Size() > 0) { 192 return fStack.StatementAt(fStack.Size()-1); 193 } 194 return NULL; 195 } 196 197 void PPDParser::Pop() 198 { 199 fStack.Remove(Top()); 200 } 201 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 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 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 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 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 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 386 PPD* PPDParser::ParseAll() 387 { 388 return Parse(true); 389 } 390 391 PPD* PPDParser::ParseHeader() 392 { 393 return Parse(false); 394 } 395 396