1 // Copyright (c) 1996, 1997 James Clark
2 // See the file copying.txt for copying permission.
6 #include "StorageManager.h"
7 #include "DssslAppMessages.h"
8 #include "StyleEngine.h"
13 #include "InputSource.h"
14 #include "jade_version.h"
15 #include "ArcEngine.h"
17 #include "MessageTable.h"
28 #ifdef DSSSL_NAMESPACE
29 namespace DSSSL_NAMESPACE {
32 DssslApp::DssslApp(int unitsPerInch)
33 : GroveApp("unicode"), unitsPerInch_(unitsPerInch),
34 dssslSpecOption_(0), debugMode_(0), dsssl2_(0),
39 registerOption('d', SP_T("dsssl_spec"));
40 registerOption('V', SP_T("variable[=value]"));
44 int DssslApp::init(int argc, AppChar **argv)
46 int ret = GroveApp::init(argc, argv);
49 // Since we use strtod(), must have C numeric
50 setlocale(LC_NUMERIC, "C");
53 MessageTable::instance()->registerMessageDomain(jstyleModule,
54 OPENJADE_MESSAGE_DOMAIN,
59 int DssslApp::processSysid(const StringC &sysid)
61 rootSystemId_ = sysid;
63 if (!entityManager()->parseSystemId(sysid, systemCharset(), 0, 0,
66 for (size_t i = v.size(); i > 0; i--)
67 if (v[i - 1].storageManager->inheritable()) {
68 ParsedSystemId specId;
70 StorageObjectSpec &spec = specId[0];
72 StringC &s = spec.specId;
73 // replace an up to 5 character extension with .dsl
74 for (size_t j = 0; j < 5; j++) {
77 if (s[s.size() - j - 1] == '.') {
78 s.resize(s.size() - j - 1);
82 if (strcmp(v[i - 1].storageManager->type(), "OSFILE") == 0)
83 defaultOutputBasename_ = s;
84 if (!dssslSpecOption_) {
85 static const Char ext[] = { '.', 'd', 's', 'l' };
86 s.append(ext, SIZEOF(ext));
87 specId.unparse(systemCharset(), 0, dssslSpecSysid_);
91 return GroveApp::processSysid(sysid);
94 void DssslApp::processOption(AppChar opt, const AppChar *arg)
104 dssslSpecId_.resize(0);
105 dssslSpecSysid_ = convertInput(arg);
106 dssslSpecOption_ = 1;
107 splitOffId(dssslSpecSysid_, dssslSpecId_);
110 defineVars_.push_back(convertInput(arg));
116 message(DssslAppMessages::versionInfo,
117 StringMessageArg(convertInput(SP_T(OPENJADE_PACKAGE))),
118 StringMessageArg(convertInput(SP_T(OPENJADE_VERSION))));
121 GroveApp::processOption(opt, arg);
125 void DssslApp::splitOffId(StringC &sysid, StringC &id)
128 for (size_t i = sysid.size(); i > 0; i--) {
129 if (sysid[i - 1] == '#') {
130 id.assign(sysid.data() + i,
138 int DssslApp::generateEvents(ErrorCountEventHandler *eceh)
140 groveTable_.insert(rootSystemId_, rootNode_);
141 // Since the thread parsing the DSSSL spec is a different thread
142 // from the thread parsing the document, we can't share an
144 // The document parser has already been inited and so will
145 // use the current entity manager.
146 // The spec parser hasn't yet been inited and so will use
147 // a new entity manager.
148 // The parser thread is started in GroveApp::generateEvents
149 // which hasn't happened yet.
150 clearEntityManager();
151 return GroveApp::generateEvents(eceh);
154 Boolean DssslApp::getDssslSpecFromGrove()
157 if (rootNode_->getProlog(nl) != accessOK)
161 if (nl->first(nd) != accessOK)
164 if (nd->getSystemData(pi) == accessOK) {
166 const LocNode *lnd = LocNode::convert(nd);
168 lnd->getLocation(loc);
169 if (getDssslSpecFromPi(pi.data(), pi.size(), loc))
172 if (nl.assignRest() != accessOK)
178 Boolean DssslApp::getDssslSpecFromPi(const Char *s, size_t n,
183 Boolean (DssslApp::*handler)(const Char *s, size_t, const Location &);
185 { "xml-stylesheet", &DssslApp::handleAttlistPi },
186 { "xml:stylesheet", &DssslApp::handleAttlistPi },
187 { "stylesheet", &DssslApp::handleAttlistPi },
188 { "dsssl", &DssslApp::handleSimplePi },
190 for (size_t i = 0; i < SIZEOF(pis); i++) {
191 size_t len = strlen(pis[i].key);
193 && matchCi(s, len, pis[i].key)
194 && (n == len || isS(s[len]))) {
197 return (this->*pis[i].handler)(s, n, loc);
203 Boolean DssslApp::handleSimplePi(const Char *s, size_t n,
210 splitOffId(sysid, dssslSpecId_);
211 return entityManager()->expandSystemId(sysid, loc, 0, systemCharset(), 0, *this,
215 Boolean DssslApp::handleAttlistPi(const Char *s, size_t n,
218 // FIXME maybe give warnings if syntax is wrong
224 while (getAttribute(s, n, name, value)) {
225 if (matchCi(name, "type")) {
226 static const char *types[] = {
230 "application/x-dsssl"
232 for (size_t i = 0; i < SIZEOF(types); i++)
233 if (matchCi(value, types[i])) {
240 else if (matchCi(name, "href")) {
245 if (!isDsssl || !hadHref)
247 splitOffId(href, dssslSpecId_);
248 // FIXME should use location of attribute value rather than location of PI
249 return entityManager()->expandSystemId(href, loc, 0, systemCharset(), 0, *this,
253 void DssslApp::skipS(const Char *&s, size_t &n)
255 while (n > 0 && isS(*s))
259 Boolean DssslApp::isS(Char c)
261 return c <= CHAR_MAX && isspace((unsigned char)c);
264 Boolean DssslApp::matchCi(const StringC &s, const char *key)
266 return matchCi(s.data(), s.size(), key);
269 Boolean DssslApp::matchCi(const Char *s, size_t n, const char *key)
271 for (; *key; key++, s++, n--) {
274 if (*s != tolower(*key) && *s != toupper(*key))
280 Boolean DssslApp::getAttribute(const Char *&s, size_t &n,
281 StringC &name, StringC &value)
289 if (*s == '=' || isS(*s))
295 if (n == 0 || *s != '=')
300 if (n > 0 && (*s == '"' || *s == '\'')) {
321 // FIXME resolve numeric character references
325 Boolean DssslApp::initSpecParser()
327 if (!dssslSpecOption_ && !getDssslSpecFromGrove() && dssslSpecSysid_.size() == 0) {
328 message(DssslAppMessages::noSpec);
331 SgmlParser::Params params;
332 params.sysid = dssslSpecSysid_;
333 params.entityManager = entityManager().pointer();
334 params.options = &options_;
335 specParser_.init(params);
336 specParser_.allLinkTypesActivated();
340 void DssslApp::processGrove()
342 if (!initSpecParser())
344 const FOTBuilder::Extension *extensions = 0;
345 Owner<FOTBuilder> fotb(makeFOTBuilder(extensions));
348 StyleEngine se(*this, *this, unitsPerInch_, debugMode_,
349 dsssl2_, strictMode_, extensions);
350 for (size_t i = 0; i < defineVars_.size(); i++)
351 se.defineVariable(defineVars_[i]);
352 se.parseSpec(specParser_, systemCharset(), dssslSpecId_, *this);
353 se.process(rootNode_, *fotb);
356 bool DssslApp::load(const StringC &sysid, const Vector<StringC> &active,
357 const NodePtr &parent, NodePtr &rootNode, const Vector<StringC> &architecture)
359 SgmlParser::Params params;
360 params.sysid = sysid;
361 const NodePtr *ndp = groveTable_.lookup(params.sysid);
366 ErrorCountEventHandler *eceh;
367 const SdNode *sdNode;
370 && parent->getGroveRoot(parentRoot) == accessOK
371 && (sdNode = SdNode::convert(parentRoot)) != 0
372 && sdNode->getSd(params.sd, params.prologSyntax, params.instanceSyntax) == accessOK) {
373 params.entityType = SgmlParser::Params::subdoc;
374 eceh = GroveBuilder::make(groveTable_.count() + 1, this, this, 0,
375 params.sd, params.prologSyntax, params.instanceSyntax,
379 eceh = GroveBuilder::make(groveTable_.count() + 1, this, this, 0, rootNode);
380 Owner<EventHandler> eh(eceh);
381 groveTable_.insert(params.sysid, rootNode);
382 params.entityManager = entityManager().pointer();
383 params.options = &options_;
387 for (size_t i = 0; i < active.size(); i++)
388 parser.activateLinkType(active[i]);
389 parser.allLinkTypesActivated();
391 if (architecture.size() > 0) {
392 SelectOneArcDirector director(architecture, *eh);
393 ArcEngine::parseAll(parser, director, director, eceh->cancelPtr());
396 parser.parseAll(*eh, eceh->cancelPtr());
400 void DssslApp::mapSysid(StringC &sysid)
402 // map a sysid according to SYSTEM catalog entries.
403 ConstPtr<EntityCatalog>
404 catalog(entityManager()->makeCatalog(sysid, systemCharset(), *this));
407 txt.addChars(sysid, loc);
409 extid.setSystem(txt);
411 ExternalTextEntity ent(name, EntityDecl::generalEntity, loc, extid);
412 catalog->lookup(ent, *(parser().instanceSyntax()), systemCharset(),
416 bool DssslApp::readEntity(const StringC &sysid, StringC &contents)
418 Owner<InputSource> in(entityManager()->open(sysid,
420 InputSourceOrigin::make(),
426 Xchar c = in->get(*this);
427 if (c == InputSource::eE)
429 in->extendToBufferEnd();
430 contents.append(in->currentTokenStart(), in->currentTokenLength());
432 return !in->accessError();
435 #ifdef DSSSL_NAMESPACE