* Copyright 2008, Haiku.
* Distributed under the terms of the MIT license.
*
* Authors:
* Michael Pfeiffer <laplace@users.sourceforge.net>
*/
#include "PPDParser.h"
#include "AutoDelete.h"
#include <stdio.h>
#include <stdlib.h>
struct Keyword {
const char* name;
const char* since;
int major;
int minor;
bool found;
};
static const Keyword gRequiredKeywords[] = {
{"DefaultImageableArea", NULL},
{"DefaultPageRegion", NULL},
{"DefaultPageSize", NULL},
{"DefaultPaperDimension", NULL},
{"ImageableArea", NULL},
{"LanguageEncoding", "4.3"},
{"LanguageVersion", NULL},
{"Manufacturer", "4.3"},
{"ModelName", NULL},
{"NickName", NULL},
{"PageRegion", NULL},
{"PageSize", NULL},
{"PaperDimension", NULL},
{"PCFileName", NULL},
{"PPD-Adobe", NULL},
{"Product", NULL},
{"PSVersion", NULL},
};
const char* kPPDAdobe = "PPD-Adobe";
#define NUMBER_OF_REQUIRED_KEYWORDS (int)(sizeof(gRequiredKeywords) / sizeof(struct Keyword))
class RequiredKeywords
{
private:
Keyword fKeywords[NUMBER_OF_REQUIRED_KEYWORDS];
bool fGotVersion;
int fMajorVersion;
int fMinorVersion;
void ExtractVersion(int& major, int& minor, const char* version);
bool IsVersionRequired(Keyword* keyword);
public:
RequiredKeywords();
bool IsRequired(Statement* statement);
bool IsComplete();
void AppendMissing(BString* string);
};
RequiredKeywords::RequiredKeywords()
: fGotVersion(false)
{
for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
fKeywords[i] = gRequiredKeywords[i];
fKeywords[i].found = false;
const char* since = fKeywords[i].since;
if (since != NULL) {
ExtractVersion(fKeywords[i].major, fKeywords[i].minor, since);
}
}
}
void RequiredKeywords::ExtractVersion(int& major, int& minor, const char* version)
{
major = atoi(version);
minor = 0;
version = strchr(version, '.');
if (version != NULL) {
version ++;
minor = atoi(version);
}
}
bool RequiredKeywords::IsVersionRequired(Keyword* keyword)
{
if (keyword->since == NULL) return true;
if (!fGotVersion) return true;
if (fMajorVersion < keyword->major) return false;
if (fMajorVersion == keyword->major &&
fMinorVersion < keyword->minor) return false;
return true;
}
bool RequiredKeywords::IsRequired(Statement* statement)
{
const char* keyword = statement->GetKeyword()->String();
if (!fGotVersion && strcmp(kPPDAdobe, keyword) == 0 &&
statement->GetValue() != NULL) {
Value* value = statement->GetValue();
BString* string = value->GetValue();
fGotVersion = true;
ExtractVersion(fMajorVersion, fMinorVersion, string->String());
}
BString defaultKeyword;
if (statement->GetType() == Statement::kDefault) {
defaultKeyword << "Default" << keyword;
keyword = defaultKeyword.String();
}
for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
const char* name = fKeywords[i].name;
if (strcmp(name, keyword) == 0) {
fKeywords[i].found = true;
return true;
}
}
return false;
}
bool RequiredKeywords::IsComplete()
{
for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
if (!fKeywords[i].found && IsVersionRequired(&fKeywords[i])) {
return false;
}
}
return true;
}
void RequiredKeywords::AppendMissing(BString* string)
{
for (int i = 0; i < NUMBER_OF_REQUIRED_KEYWORDS; i ++) {
if (!fKeywords[i].found && IsVersionRequired(&fKeywords[i])) {
*string << "Keyword " << fKeywords[i].name;
if (fKeywords[i].since != NULL) {
*string << fKeywords[i].major << ". " << fKeywords[i].minor
<< " < " <<
fMajorVersion << "." << fMinorVersion << " ";
}
*string << " is missing\n";
}
}
}
static const char* kEndStatement = "End";
PPDParser::PPDParser(const char* file)
: Parser(file)
, fStack(false)
, fRequiredKeywords(new RequiredKeywords)
{
}
PPDParser::~PPDParser()
{
delete fRequiredKeywords;
}
void PPDParser::Push(Statement* statement)
{
fStack.Add(statement);
}
Statement* PPDParser::Top()
{
if (fStack.Size() > 0) {
return fStack.StatementAt(fStack.Size()-1);
}
return NULL;
}
void PPDParser::Pop()
{
fStack.Remove(Top());
}
void PPDParser::AddStatement(Statement* statement)
{
fRequiredKeywords->IsRequired(statement);
Statement* top = Top();
if (top != NULL) {
top->AddChild(statement);
} else {
fPPD->Add(statement);
}
}
bool PPDParser::IsValidOpenStatement(GroupStatement* statement)
{
if (statement->GetGroupName() == NULL) {
Error("Missing group ID in open statement");
return false;
}
return true;
}
bool PPDParser::IsValidCloseStatement(GroupStatement* statement)
{
if (statement->GetGroupName() == NULL) {
Error("Missing option in close statement");
return false;
}
if (Top() == NULL) {
Error("Close statement without an open statement");
return false;
}
GroupStatement openStatement(Top());
BString open = openStatement.GetKeyword();
open.RemoveFirst("Open");
BString close = statement->GetKeyword();
close.RemoveFirst("Close");
if (open != close) {
Error("Close statement has no corresponding open statement");
#ifdef VERBOSE
printf("********* OPEN ************\n");
openStatement.GetStatement()->Print();
printf("********* CLOSE ***********\n");
statement->GetStatement()->Print();
#endif
return false;
}
BString openValue(openStatement.GetGroupName());
BString closeValue(statement->GetGroupName());
const char* whiteSpaces = " \t";
openValue.RemoveSet(whiteSpaces);
closeValue.RemoveSet(whiteSpaces);
if (openValue != closeValue) {
BString message("Open name does not match close name ");
message << openValue << " != " << closeValue << "\n";
Warning(message.String());
}
return true;
}
bool PPDParser::ParseStatement(Statement* _statement)
{
AutoDelete<Statement> statement(_statement);
if (_statement->GetKeyword() == NULL) {
Error("Keyword missing");
return false;
}
if (_statement->GetOption() != NULL &&
_statement->GetOption()->GetValue() == NULL) {
Error("Option has no value");
return false;
}
if (_statement->GetValue() != NULL &&
_statement->GetValue()->GetValue() == NULL) {
Error("Value has no value");
return false;
}
const char* keyword = statement.Get()->GetKeyword()->String();
if (strcmp(keyword, kEndStatement) == 0) {
return true;
}
GroupStatement group(statement.Get());
if (group.IsOpenGroup()) {
if (!IsValidOpenStatement(&group)) {
return false;
}
AddStatement(statement.Release());
Push(statement.Get());
return true;
}
if (group.IsCloseGroup()) {
if (!IsValidCloseStatement(&group)) {
return false;
}
Pop();
return true;
}
AddStatement(statement.Release());
return true;
}
bool PPDParser::ParseStatements()
{
Statement* statement;
while ((statement = Parser::Parse()) != NULL) {
#ifdef VERBOSE
statement->Print(); fflush(stdout);
#endif
if (!ParseStatement(statement)) {
return false;
}
if (!fParseAll && fRequiredKeywords->IsComplete()) {
break;
}
}
if (HasError()) {
return false;
}
if (Top() != NULL) {
BString error("Missing close statement for:\n");
do {
error << " * " <<
Top()->GetKeywordString() << " " <<
Top()->GetOptionString() << "\n";
Pop();
} while (Top() != NULL);
Error(error.String());
return false;
}
return true;
}
PPD* PPDParser::Parse(bool all)
{
fParseAll = all;
if (InitCheck() != B_OK) return NULL;
fPPD = new PPD();
ParseStatements();
if (!HasError() && !fRequiredKeywords->IsComplete()) {
BString string;
fRequiredKeywords->AppendMissing(&string);
Error(string.String());
}
if (HasError()) {
delete fPPD; fPPD = NULL;
return NULL;
}
return fPPD;
}
PPD* PPDParser::ParseAll()
{
return Parse(true);
}
PPD* PPDParser::ParseHeader()
{
return Parse(false);
}