1 // Copyright (c) 1996, 1997 James Clark
2 // See the file copying.txt for copying permission.
3
4 #include "stylelib.h"
5 #include "DssslApp.h"
6 #include "StorageManager.h"
7 #include "DssslAppMessages.h"
8 #include "StyleEngine.h"
9 #include "sptchar.h"
10 #include "macros.h"
11 #include "LocNode.h"
12 #include "SdNode.h"
13 #include "InputSource.h"
14 #include "jade_version.h"
15 #include "ArcEngine.h"
16
17 #include <ctype.h>
18 #include <string.h>
19
20 #ifdef SP_HAVE_LOCALE
21 #include <locale.h>
22 #endif
23
24 #ifdef DSSSL_NAMESPACE
25 namespace DSSSL_NAMESPACE {
26 #endif
27
DssslApp(int unitsPerInch)28 DssslApp::DssslApp(int unitsPerInch)
29 : GroveApp("unicode"), unitsPerInch_(unitsPerInch),
30 dssslSpecOption_(0), debugMode_(0), dsssl2_(0)
31 {
32 registerOption('G');
33 registerOption('2');
34 registerOption('d', SP_T("dsssl_spec"));
35 registerOption('V', SP_T("variable"));
36 }
37
init(int argc,AppChar ** argv)38 int DssslApp::init(int argc, AppChar **argv)
39 {
40 int ret = GroveApp::init(argc, argv);
41 #ifdef SP_HAVE_LOCALE
42 // Since we use strtod(), must have C numeric
43 setlocale(LC_NUMERIC, "C");
44 #endif
45 return ret;
46 }
47
processSysid(const StringC & sysid)48 int DssslApp::processSysid(const StringC &sysid)
49 {
50 rootSystemId_ = sysid;
51 ParsedSystemId v;
52 if (!entityManager()->parseSystemId(sysid, systemCharset(), 0, 0,
53 *this, v))
54 return 0;
55 for (size_t i = v.size(); i > 0; i--)
56 if (v[i - 1].storageManager->inheritable()) {
57 ParsedSystemId specId;
58 specId.resize(1);
59 StorageObjectSpec &spec = specId[0];
60 spec = v[i - 1];
61 StringC &s = spec.specId;
62 // replace an up to 5 character extension with .dsl
63 for (size_t j = 0; j < 5; j++) {
64 if (s.size() < j + 1)
65 break;
66 if (s[s.size() - j - 1] == '.') {
67 s.resize(s.size() - j - 1);
68 break;
69 }
70 }
71 if (strcmp(v[i - 1].storageManager->type(), "OSFILE") == 0)
72 defaultOutputBasename_ = s;
73 if (!dssslSpecOption_) {
74 static const Char ext[] = { '.', 'd', 's', 'l' };
75 s.append(ext, SIZEOF(ext));
76 specId.unparse(systemCharset(), 0, dssslSpecSysid_);
77 }
78 break;
79 }
80 return GroveApp::processSysid(sysid);
81 }
82
processOption(AppChar opt,const AppChar * arg)83 void DssslApp::processOption(AppChar opt, const AppChar *arg)
84 {
85 switch (opt) {
86 case 'G':
87 debugMode_ = 1;
88 break;
89 case '2':
90 dsssl2_ = 1;
91 break;
92 case 'd':
93 dssslSpecId_.resize(0);
94 dssslSpecSysid_ = convertInput(arg);
95 dssslSpecOption_ = 1;
96 splitOffId(dssslSpecSysid_, dssslSpecId_);
97 break;
98 case 'V':
99 defineVars_.push_back(convertInput(arg));
100 break;
101 case 'v':
102 message(DssslAppMessages::versionInfo,
103 StringMessageArg(convertInput(JADE_VERSION)));
104 // fall through
105 default:
106 GroveApp::processOption(opt, arg);
107 }
108 }
109
splitOffId(StringC & sysid,StringC & id)110 void DssslApp::splitOffId(StringC &sysid, StringC &id)
111 {
112 id.resize(0);
113 for (size_t i = sysid.size(); i > 0; i--) {
114 if (sysid[i - 1] == '#') {
115 id.assign(sysid.data() + i,
116 sysid.size() - i);
117 sysid.resize(i - 1);
118 break;
119 }
120 }
121 }
122
generateEvents(ErrorCountEventHandler * eceh)123 int DssslApp::generateEvents(ErrorCountEventHandler *eceh)
124 {
125 groveTable_.insert(rootSystemId_, rootNode_);
126 // Since the thread parsing the DSSSL spec is a different thread
127 // from the thread parsing the document, we can't share an
128 // entity manager.
129 // The document parser has already been inited and so will
130 // use the current entity manager.
131 // The spec parser hasn't yet been inited and so will use
132 // a new entity manager.
133 // The parser thread is started in GroveApp::generateEvents
134 // which hasn't happened yet.
135 clearEntityManager();
136 return GroveApp::generateEvents(eceh);
137 }
138
getDssslSpecFromGrove()139 Boolean DssslApp::getDssslSpecFromGrove()
140 {
141 NodeListPtr nl;
142 if (rootNode_->getProlog(nl) != accessOK)
143 return 0;
144 for (;;) {
145 NodePtr nd;
146 if (nl->first(nd) != accessOK)
147 break;
148 GroveString pi;
149 if (nd->getSystemData(pi) == accessOK) {
150 Location loc;
151 const LocNode *lnd = LocNode::convert(nd);
152 if (lnd)
153 lnd->getLocation(loc);
154 if (getDssslSpecFromPi(pi.data(), pi.size(), loc))
155 return 1;
156 }
157 if (nl.assignRest() != accessOK)
158 break;
159 }
160 return 0;
161 }
162
getDssslSpecFromPi(const Char * s,size_t n,const Location & loc)163 Boolean DssslApp::getDssslSpecFromPi(const Char *s, size_t n,
164 const Location &loc)
165 {
166 static struct {
167 const char *key;
168 Boolean (DssslApp::*handler)(const Char *s, size_t, const Location &);
169 } pis[] = {
170 { "xml-stylesheet", &DssslApp::handleAttlistPi },
171 { "xml:stylesheet", &DssslApp::handleAttlistPi },
172 { "stylesheet", &DssslApp::handleAttlistPi },
173 { "dsssl", &DssslApp::handleSimplePi },
174 };
175 for (size_t i = 0; i < SIZEOF(pis); i++) {
176 size_t len = strlen(pis[i].key);
177 if (n >= len
178 && matchCi(s, len, pis[i].key)
179 && (n == len || isS(s[len]))) {
180 s += len;
181 n -= len;
182 return (this->*pis[i].handler)(s, n, loc);
183 }
184 }
185 return 0;
186 }
187
handleSimplePi(const Char * s,size_t n,const Location & loc)188 Boolean DssslApp::handleSimplePi(const Char *s, size_t n,
189 const Location &loc)
190 {
191 skipS(s, n);
192 if (n == 0)
193 return 0;
194 StringC sysid(s, n);
195 splitOffId(sysid, dssslSpecId_);
196 return entityManager()->expandSystemId(sysid, loc, 0, systemCharset(), 0, *this,
197 dssslSpecSysid_);
198 }
199
handleAttlistPi(const Char * s,size_t n,const Location & loc)200 Boolean DssslApp::handleAttlistPi(const Char *s, size_t n,
201 const Location &loc)
202 {
203 // FIXME maybe give warnings if syntax is wrong
204 Boolean hadHref = 0;
205 StringC href;
206 Boolean isDsssl = 0;
207 StringC name;
208 StringC value;
209 while (getAttribute(s, n, name, value)) {
210 if (matchCi(name, "type")) {
211 static const char *types[] = {
212 "text/dsssl",
213 "text/x-dsssl",
214 "application/dsssl",
215 "application/x-dsssl"
216 };
217 for (size_t i = 0; i < SIZEOF(types); i++)
218 if (matchCi(value, types[i])) {
219 isDsssl = 1;
220 break;
221 }
222 if (!isDsssl)
223 return 0;
224 }
225 else if (matchCi(name, "href")) {
226 hadHref = 1;
227 value.swap(href);
228 }
229 }
230 if (!isDsssl || !hadHref)
231 return 0;
232 splitOffId(href, dssslSpecId_);
233 // FIXME should use location of attribute value rather than location of PI
234 return entityManager()->expandSystemId(href, loc, 0, systemCharset(), 0, *this,
235 dssslSpecSysid_);
236 }
237
skipS(const Char * & s,size_t & n)238 void DssslApp::skipS(const Char *&s, size_t &n)
239 {
240 while (n > 0 && isS(*s))
241 s++, n--;
242 }
243
isS(Char c)244 Boolean DssslApp::isS(Char c)
245 {
246 return c <= CHAR_MAX && isspace((unsigned char)c);
247 }
248
matchCi(const StringC & s,const char * key)249 Boolean DssslApp::matchCi(const StringC &s, const char *key)
250 {
251 return matchCi(s.data(), s.size(), key);
252 }
253
matchCi(const Char * s,size_t n,const char * key)254 Boolean DssslApp::matchCi(const Char *s, size_t n, const char *key)
255 {
256 for (; *key; key++, s++, n--) {
257 if (!n)
258 return 0;
259 if (*s != tolower(*key) && *s != toupper(*key))
260 return 0;
261 }
262 return n == 0;
263 }
264
getAttribute(const Char * & s,size_t & n,StringC & name,StringC & value)265 Boolean DssslApp::getAttribute(const Char *&s, size_t &n,
266 StringC &name, StringC &value)
267 {
268 name.resize(0);
269 value.resize(0);
270 skipS(s, n);
271 for (;;) {
272 if (n == 0)
273 return 0;
274 if (*s == '=' || isS(*s))
275 break;
276 name += *s;
277 s++, n--;
278 }
279 skipS(s, n);
280 if (n == 0 || *s != '=')
281 return 0;
282 s++, n--;
283 skipS(s, n);
284 Char quote = 0;
285 if (n > 0 && (*s == '"' || *s == '\'')) {
286 quote = *s;
287 s++, n--;
288 }
289 for (;;) {
290 if (n == 0) {
291 if (quote)
292 return 0;
293 break;
294 }
295 if (quote) {
296 if (*s == quote) {
297 s++, n--;
298 break;
299 }
300 }
301 else if (isS(*s))
302 break;
303 value += *s;
304 s++, n--;
305 }
306 // FIXME resolve numeric character references
307 return 1;
308 }
309
initSpecParser()310 Boolean DssslApp::initSpecParser()
311 {
312 if (!dssslSpecOption_ && !getDssslSpecFromGrove() && dssslSpecSysid_.size() == 0) {
313 message(DssslAppMessages::noSpec);
314 return 0;
315 }
316 SgmlParser::Params params;
317 params.sysid = dssslSpecSysid_;
318 params.entityManager = entityManager().pointer();
319 params.options = &options_;
320 specParser_.init(params);
321 specParser_.allLinkTypesActivated();
322 return 1;
323 }
324
processGrove()325 void DssslApp::processGrove()
326 {
327 if (!initSpecParser())
328 return;
329 const FOTBuilder::Extension *extensions = 0;
330 Owner<FOTBuilder> fotb(makeFOTBuilder(extensions));
331 if (!fotb)
332 return;
333 StyleEngine se(*this, *this, unitsPerInch_, debugMode_, dsssl2_, extensions);
334 for (size_t i = 0; i < defineVars_.size(); i++)
335 se.defineVariable(defineVars_[i]);
336 se.parseSpec(specParser_, systemCharset(), dssslSpecId_, *this);
337 se.process(rootNode_, *fotb);
338 }
339
load(const StringC & sysid,const Vector<StringC> & active,const NodePtr & parent,NodePtr & rootNode,const Vector<StringC> & architecture)340 bool DssslApp::load(const StringC &sysid, const Vector<StringC> &active,
341 const NodePtr &parent, NodePtr &rootNode, const Vector<StringC> &architecture)
342 {
343 SgmlParser::Params params;
344 params.sysid = sysid;
345 const NodePtr *ndp = groveTable_.lookup(params.sysid);
346 if (ndp) {
347 rootNode = *ndp;
348 return 1;
349 }
350 ErrorCountEventHandler *eceh;
351 const SdNode *sdNode;
352 NodePtr parentRoot;
353 if (parent
354 && parent->getGroveRoot(parentRoot) == accessOK
355 && (sdNode = SdNode::convert(parentRoot)) != 0
356 && sdNode->getSd(params.sd, params.prologSyntax, params.instanceSyntax) == accessOK) {
357 params.entityType = SgmlParser::Params::subdoc;
358 eceh = GroveBuilder::make(groveTable_.count() + 1, this, this, 0,
359 params.sd, params.prologSyntax, params.instanceSyntax,
360 rootNode);
361 }
362 else
363 eceh = GroveBuilder::make(groveTable_.count() + 1, this, this, 0, rootNode);
364 Owner<EventHandler> eh(eceh);
365 groveTable_.insert(params.sysid, rootNode);
366 params.entityManager = entityManager().pointer();
367 params.options = &options_;
368 SgmlParser parser;
369 parser.init(params);
370
371 for (size_t i = 0; i < active.size(); i++)
372 parser.activateLinkType(active[i]);
373 parser.allLinkTypesActivated();
374
375 if (architecture.size() > 0) {
376 SelectOneArcDirector director(architecture, *eh);
377 ArcEngine::parseAll(parser, director, director, eceh->cancelPtr());
378 }
379 else
380 parser.parseAll(*eh, eceh->cancelPtr());
381 return 1;
382 }
383
readEntity(const StringC & sysid,StringC & contents)384 bool DssslApp::readEntity(const StringC &sysid, StringC &contents)
385 {
386 Owner<InputSource> in(entityManager()->open(sysid,
387 systemCharset(),
388 InputSourceOrigin::make(),
389 0,
390 *this));
391 if (!in)
392 return 0;
393 for (;;) {
394 Xchar c = in->get(*this);
395 if (c == InputSource::eE)
396 break;
397 in->extendToBufferEnd();
398 contents.append(in->currentTokenStart(), in->currentTokenLength());
399 }
400 return !in->accessError();
401 }
402
403 #ifdef DSSSL_NAMESPACE
404 }
405 #endif
406