Initial commit
[profile/ivi/openjade.git] / style / DssslApp.cxx
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 #include "Entity.h"
17 #include "MessageTable.h"
18
19 #include <ctype.h>
20 #include <string.h>
21
22 #ifdef SP_HAVE_LOCALE
23 #ifdef SP_HAVE_WCHAR
24 #include <locale.h>
25 #endif
26 #endif
27
28 #ifdef DSSSL_NAMESPACE
29 namespace DSSSL_NAMESPACE {
30 #endif
31
32 DssslApp::DssslApp(int unitsPerInch)
33 : GroveApp("unicode"), unitsPerInch_(unitsPerInch),
34   dssslSpecOption_(0), debugMode_(0), dsssl2_(0),
35   strictMode_(0)
36 {
37   registerOption('G');
38   registerOption('2');
39   registerOption('d', SP_T("dsssl_spec"));
40   registerOption('V', SP_T("variable[=value]"));
41   registerOption('s');
42 }
43
44 int DssslApp::init(int argc, AppChar **argv)
45 {
46   int ret = GroveApp::init(argc, argv);
47 #ifdef SP_HAVE_LOCALE
48 #ifdef SP_HAVE_WCHAR
49   // Since we use strtod(), must have C numeric
50   setlocale(LC_NUMERIC, "C");
51 #endif
52 #endif
53   MessageTable::instance()->registerMessageDomain(jstyleModule,
54                                                 OPENJADE_MESSAGE_DOMAIN,
55                                                 OPENJADE_LOCALE_DIR);
56   return ret;
57 }
58
59 int DssslApp::processSysid(const StringC &sysid)
60 {
61   rootSystemId_ = sysid;
62   ParsedSystemId v;
63   if (!entityManager()->parseSystemId(sysid, systemCharset(), 0, 0,
64                                       *this, v))
65     return 0;
66   for (size_t i = v.size(); i > 0; i--)
67     if (v[i - 1].storageManager->inheritable()) {
68       ParsedSystemId specId;
69       specId.resize(1);
70       StorageObjectSpec &spec = specId[0];
71       spec = v[i - 1];
72       StringC &s = spec.specId;
73       // replace an up to 5 character extension with .dsl
74       for (size_t j = 0; j < 5; j++) {
75         if (s.size() < j + 1)
76           break;
77         if (s[s.size() - j - 1] == '.') {
78           s.resize(s.size() - j - 1);
79           break;
80         }
81       }
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_);
88       }
89       break;
90     }
91   return GroveApp::processSysid(sysid);
92 }
93
94 void DssslApp::processOption(AppChar opt, const AppChar *arg)
95 {
96   switch (opt) {
97   case 'G':
98     debugMode_ = 1;
99     break;
100   case '2':
101     dsssl2_ = 1;
102     break;
103   case 'd':
104     dssslSpecId_.resize(0);
105     dssslSpecSysid_ = convertInput(arg);
106     dssslSpecOption_ = 1;
107     splitOffId(dssslSpecSysid_, dssslSpecId_);
108     break;
109   case 'V':
110     defineVars_.push_back(convertInput(arg));
111     break;
112   case 's':
113     strictMode_ = 1;
114     break;
115   case 'v':
116     message(DssslAppMessages::versionInfo,
117             StringMessageArg(convertInput(SP_T(OPENJADE_PACKAGE))),
118             StringMessageArg(convertInput(SP_T(OPENJADE_VERSION))));
119     // fall through
120   default:
121     GroveApp::processOption(opt, arg);
122   }
123 }
124
125 void DssslApp::splitOffId(StringC &sysid, StringC &id)
126 {
127   id.resize(0);
128   for (size_t i = sysid.size(); i > 0; i--) {
129     if (sysid[i - 1] == '#') {
130       id.assign(sysid.data() + i,
131                 sysid.size() - i);
132       sysid.resize(i - 1);
133       break;
134     }
135   }
136 }
137
138 int DssslApp::generateEvents(ErrorCountEventHandler *eceh)
139 {
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
143   // entity manager.
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);
152 }
153
154 Boolean DssslApp::getDssslSpecFromGrove()
155 {
156   NodeListPtr nl;
157   if (rootNode_->getProlog(nl) != accessOK)
158     return 0;
159   for (;;) {
160     NodePtr nd;
161     if (nl->first(nd) != accessOK)
162       break;
163     GroveString pi;
164     if (nd->getSystemData(pi) == accessOK) {
165       Location loc;
166       const LocNode *lnd = LocNode::convert(nd);
167       if (lnd)
168         lnd->getLocation(loc);
169       if (getDssslSpecFromPi(pi.data(), pi.size(), loc))
170         return 1;
171     }
172     if (nl.assignRest() != accessOK)
173       break;
174   }
175   return 0;
176 }
177
178 Boolean DssslApp::getDssslSpecFromPi(const Char *s, size_t n,
179                                      const Location &loc)
180 {
181   static struct {
182     const char *key;
183     Boolean (DssslApp::*handler)(const Char *s, size_t, const Location &);
184   } pis[] = {
185     { "xml-stylesheet", &DssslApp::handleAttlistPi },
186     { "xml:stylesheet", &DssslApp::handleAttlistPi },
187     { "stylesheet", &DssslApp::handleAttlistPi },
188     { "dsssl", &DssslApp::handleSimplePi },
189   };
190   for (size_t i = 0; i < SIZEOF(pis); i++) {
191     size_t len = strlen(pis[i].key);
192     if (n >= len
193         && matchCi(s, len, pis[i].key)
194         && (n == len || isS(s[len]))) {
195       s += len;
196       n -= len;
197       return (this->*pis[i].handler)(s, n, loc);
198     }
199   }
200   return 0;
201 }
202
203 Boolean DssslApp::handleSimplePi(const Char *s, size_t n,
204                                  const Location &loc)
205 {
206   skipS(s, n);
207   if (n == 0)
208     return 0;
209   StringC sysid(s, n);
210   splitOffId(sysid, dssslSpecId_);
211   return entityManager()->expandSystemId(sysid, loc, 0, systemCharset(), 0, *this,
212                                          dssslSpecSysid_);
213 }
214
215 Boolean DssslApp::handleAttlistPi(const Char *s, size_t n,
216                                   const Location &loc)
217 {
218   // FIXME maybe give warnings if syntax is wrong
219   Boolean hadHref = 0;
220   StringC href;
221   Boolean isDsssl = 0;
222   StringC name;
223   StringC value;
224   while (getAttribute(s, n, name, value)) {
225     if (matchCi(name, "type")) {
226       static const char *types[] = {
227         "text/dsssl",
228         "text/x-dsssl",
229         "application/dsssl",
230         "application/x-dsssl"
231       };
232       for (size_t i = 0; i < SIZEOF(types); i++)
233         if (matchCi(value, types[i])) {
234           isDsssl = 1;
235           break;
236         }
237       if (!isDsssl)
238         return 0;
239     }
240     else if (matchCi(name, "href")) {
241       hadHref = 1;
242       value.swap(href);
243     }
244   }
245   if (!isDsssl || !hadHref)
246     return 0;
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,
250                                          dssslSpecSysid_);
251 }
252
253 void DssslApp::skipS(const Char *&s, size_t &n)
254 {
255   while (n > 0 && isS(*s))
256     s++, n--;
257 }
258
259 Boolean DssslApp::isS(Char c)
260 {
261   return c <= CHAR_MAX && isspace((unsigned char)c);
262 }
263
264 Boolean DssslApp::matchCi(const StringC &s, const char *key)
265 {
266   return matchCi(s.data(), s.size(), key);
267 }
268
269 Boolean DssslApp::matchCi(const Char *s, size_t n, const char *key)
270 {
271   for (; *key; key++, s++, n--) {
272     if (!n)
273       return 0;
274     if (*s != tolower(*key) && *s != toupper(*key))
275       return 0;
276   }
277   return n == 0;
278 }
279
280 Boolean DssslApp::getAttribute(const Char *&s, size_t &n,
281                                StringC &name, StringC &value)
282 {
283   name.resize(0);
284   value.resize(0);
285   skipS(s, n);
286   for (;;) {
287     if (n == 0)
288       return 0;
289     if (*s == '=' || isS(*s))
290       break;
291     name += *s;
292     s++, n--;
293   }
294   skipS(s, n);
295   if (n == 0 || *s != '=')
296     return 0;
297   s++, n--;
298   skipS(s, n);
299   Char quote = 0;
300   if (n > 0 && (*s == '"' || *s == '\'')) {
301     quote = *s;
302     s++, n--;
303   }
304   for (;;) {
305     if (n == 0) {
306       if (quote)
307         return 0;
308       break;
309     }
310     if (quote) {
311       if (*s == quote) {
312         s++, n--;
313         break;
314       }
315     }
316     else if (isS(*s))
317       break;
318     value += *s;
319     s++, n--;
320   }
321   // FIXME resolve numeric character references
322   return 1;
323 }
324
325 Boolean DssslApp::initSpecParser()
326 {
327   if (!dssslSpecOption_ && !getDssslSpecFromGrove() && dssslSpecSysid_.size() == 0) {
328     message(DssslAppMessages::noSpec);
329     return 0;
330   }
331   SgmlParser::Params params;
332   params.sysid = dssslSpecSysid_;
333   params.entityManager = entityManager().pointer();
334   params.options = &options_;
335   specParser_.init(params);
336   specParser_.allLinkTypesActivated();
337   return 1;
338 }
339
340 void DssslApp::processGrove()
341 {
342   if (!initSpecParser())
343     return;
344   const FOTBuilder::Extension *extensions = 0;
345   Owner<FOTBuilder> fotb(makeFOTBuilder(extensions));
346   if (!fotb)
347     return;
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);
354 }
355
356 bool DssslApp::load(const StringC &sysid, const Vector<StringC> &active,
357                     const NodePtr &parent, NodePtr &rootNode, const Vector<StringC> &architecture)
358 {
359   SgmlParser::Params params;
360   params.sysid = sysid;
361   const NodePtr *ndp = groveTable_.lookup(params.sysid);
362   if (ndp) {
363     rootNode = *ndp;
364     return 1;
365   }
366   ErrorCountEventHandler *eceh;
367   const SdNode *sdNode;
368   NodePtr parentRoot;
369   if (parent
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,
376                               rootNode);
377   }
378   else
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_;
384   SgmlParser parser;
385   parser.init(params);
386
387   for (size_t i = 0; i < active.size(); i++)
388     parser.activateLinkType(active[i]);
389   parser.allLinkTypesActivated();
390
391   if (architecture.size() > 0) {
392     SelectOneArcDirector director(architecture, *eh);
393     ArcEngine::parseAll(parser, director, director, eceh->cancelPtr());
394   }
395   else
396     parser.parseAll(*eh, eceh->cancelPtr());
397   return 1;
398 }
399
400 void DssslApp::mapSysid(StringC &sysid)
401 {
402  // map a sysid according to SYSTEM catalog entries. 
403   ConstPtr<EntityCatalog> 
404      catalog(entityManager()->makeCatalog(sysid, systemCharset(), *this));
405   Text txt;
406   Location loc;
407   txt.addChars(sysid, loc);
408   ExternalId extid;
409   extid.setSystem(txt);
410   StringC name;
411   ExternalTextEntity ent(name, EntityDecl::generalEntity, loc, extid);
412   catalog->lookup(ent, *(parser().instanceSyntax()), systemCharset(), 
413                   *this, sysid); 
414 }
415
416 bool DssslApp::readEntity(const StringC &sysid, StringC &contents) 
417 {
418   Owner<InputSource> in(entityManager()->open(sysid,
419                                               systemCharset(),
420                                               InputSourceOrigin::make(),
421                                               0,
422                                               *this));
423   if (!in)
424     return 0;
425   for (;;) {
426     Xchar c = in->get(*this);
427     if (c == InputSource::eE)
428       break;
429     in->extendToBufferEnd();
430     contents.append(in->currentTokenStart(), in->currentTokenLength());
431   }
432   return !in->accessError();
433 }
434
435 #ifdef DSSSL_NAMESPACE
436 }
437 #endif