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