Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / nsgmls / SOEntityCatalog.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: SOEntityCatalog.C /main/1 1996/07/29 17:03:30 cde-hp $ */
24 // Copyright (c) 1994, 1995, 1996 James Clark
25 // See the file COPYING for copying permission.
26
27 #ifdef __GNUG__
28 #pragma implementation
29 #endif
30
31 #include "splib.h"
32 #include "CharsetInfo.h"
33 #include "MessageArg.h"
34 #include "CatalogMessages.h"
35 #include "SOEntityCatalog.h"
36 #include "EntityDecl.h"
37 #include "EntityCatalog.h"
38 #include "Message.h"
39 #include "StringC.h"
40 #include "types.h"
41 #include "HashTable.h"
42 #include "InputSource.h"
43 #include "Boolean.h"
44 #include "SubstTable.h"
45 #include "CatalogEntry.h"
46 #include "Vector.h"
47 #include "StorageManager.h"
48 #include "macros.h"
49
50 #ifdef SP_NAMESPACE
51 namespace SP_NAMESPACE {
52 #endif
53
54 class CatalogParser;
55 class SOEntityCatalog;
56
57 class SOCatalogManagerImpl : public SOCatalogManager {
58 public:
59   SOCatalogManagerImpl(const Vector<StringC> &sysids,
60                        size_t nSysidsMustExist,
61                        const CharsetInfo &sysidCharset,
62                        const CharsetInfo &catalogCharset);
63   ConstPtr<EntityCatalog> makeCatalog(StringC &systemId,
64                                       const CharsetInfo &charset,
65                                       ExtendEntityManager *,
66                                       Messenger &) const;
67   Boolean mapCatalog(ParsedSystemId &systemId,
68                      ExtendEntityManager *em,
69                      Messenger &mgr) const;
70 private:
71   void addCatalogsForDocument(CatalogParser &parser,
72                               StringC &sysid,
73                               SOEntityCatalog *,
74                               const CharsetInfo &charset,
75                               Messenger &mgr) const;
76   size_t nSystemCatalogsMustExist_;
77   Vector<StringC> systemCatalogs_;
78   CharsetInfo sysidCharset_;
79   CharsetInfo catalogCharset_;
80 };
81
82 class SOEntityCatalog : public EntityCatalog {
83 public:
84   SOEntityCatalog(Ptr<ExtendEntityManager> em);
85   typedef EntityDecl::DeclType DeclType;
86   Boolean document(const CharsetInfo &, Messenger &, StringC &) const;
87   Boolean sgmlDecl(const CharsetInfo &, Messenger &, StringC &) const;
88   Boolean defaultDoctype(const CharsetInfo &,
89                          Messenger &,
90                          StringC &,
91                          StringC &) const;
92   Boolean lookup(const EntityDecl &entity,
93                  const Syntax &,
94                  const CharsetInfo &,
95                  Messenger &,
96                  StringC &) const;
97   Boolean lookupPublic(const StringC &,
98                        const CharsetInfo &,
99                        Messenger &,
100                        StringC &) const;
101   void addPublicId(StringC &publicId, StringC &systemId, const Location &,
102                    Boolean override);
103   void addDelegate(StringC &prefix, StringC &systemId, const Location &,
104                    Boolean override);
105   void addSystemId(StringC &systemId, StringC &replSystemId, const Location &);
106   void addName(StringC &name, DeclType, StringC &systemId, const Location &,
107                Boolean override);
108   void setSgmlDecl(StringC &str, const Location &loc);
109   void setDocument(StringC &str, const Location &loc);
110   void setBase(const Location &loc);
111   void endCatalog();
112   const Ptr<ExtendEntityManager> &entityManager() {
113     return em_;
114   }
115 private:
116   SOEntityCatalog(const SOEntityCatalog &);     // undefined
117   void operator=(const SOEntityCatalog &);      // undefined
118
119   Boolean expandCatalogSystemId(const StringC &str,
120                                 const Location &loc,
121                                 size_t baseNumber,
122                                 Boolean isNdata,
123                                 const CharsetInfo &charset,
124                                 const StringC *lookupPublicId,
125                                 Messenger &mgr,
126                                 StringC &result) const;
127   const CatalogEntry *
128     findBestPublicEntry(const StringC &publicId, Boolean overrideOnly,
129                         const CharsetInfo &charset, Boolean &delegated) const;
130
131   class Table {
132   public:
133     Table();
134     const CatalogEntry *lookup(const StringC &, Boolean overrideOnly) const;
135     const CatalogEntry *lookup(const StringC &key,
136                                const SubstTable<Char> &substTable,
137                                Boolean overrideOnly) const;
138     void insert(const StringC &, const CatalogEntry &, Boolean override);
139     size_t count() const;
140   private:
141     Table(const Table &);       // undefined
142     void operator=(const Table &); // undefined
143     // These are entries that are applicable when an explicit system id
144     // was specified in the external identifier.
145     HashTable<StringC,CatalogEntry> overrideEntries_;
146     // This specifies the entries that should substitute for the
147     // overrideEntries_ when an explicit system identifier was not specified.
148     HashTable<StringC,CatalogEntry> normalEntries_;
149   };
150
151   Table publicIds_;
152   Table delegates_;
153   HashTable<StringC,CatalogEntry> systemIds_;
154   Table names_[4];
155   size_t catalogNumber_;
156   Boolean haveSgmlDecl_;
157   StringC sgmlDecl_;
158   Location sgmlDeclLoc_;
159   size_t sgmlDeclBaseNumber_;
160   StringC document_;
161   Boolean haveDocument_;
162   Location documentLoc_;
163   size_t documentBaseNumber_;
164   StringC defaultDoctype_;
165   Boolean haveCurrentBase_;
166   Vector<Location> base_;
167   Ptr<ExtendEntityManager> em_;
168 };
169
170 class CatalogParser : private Messenger {
171 public:
172   CatalogParser(const CharsetInfo &);
173   void parseCatalog(const StringC &sysid,
174                     Boolean mustExist,
175                     const CharsetInfo &sysidCharset,
176                     const CharsetInfo &catalogCharset,
177                     InputSourceOrigin *origin,
178                     SOEntityCatalog *catalog,
179                     Messenger &mgr);
180 public:
181   // Since it's a return type, it has to be public to keep some 
182   // (broken) compilers happy.
183   enum Param {
184     eofParam,
185     literalParam,
186     nameParam,
187     percentParam
188   };
189 private:
190   enum {
191     data,
192     eof,
193     nul,
194     lit,
195     lita,
196     minus,
197     s,
198     min                         // other minimum data characters
199   };
200   enum { minimumLiteral = 01 };
201
202   Messenger &messenger() { return *this; }
203   void dispatchMessage(Message &);
204   void dispatchMessage(const Message &);
205   void initMessage(Message &);
206   void parsePublic();
207   void parseDelegate();
208   void parseSystem();
209   void parseNameMap(EntityDecl::DeclType declType);
210   void parseOverride();
211   Param parseParam(unsigned flags = 0);
212   Boolean parseArg();
213   void parseLiteral(Char delim, unsigned flags);
214   void parseName();
215   void skipComment();
216   void upcase(StringC &);
217   Boolean inLoop(const Location &loc);
218   Boolean isMinimumData(Xchar c) {
219     int cat = categoryTable_[c];
220     return (cat == min || (cat == s && c != tab_)
221             || cat == minus || cat == lita);
222   }
223   Xchar get() { return in_->get(messenger()); }
224   void unget() { in_->ungetToken(); }
225   Messenger *mgr_;
226   InputSource *in_;
227   SOEntityCatalog *catalog_;
228   StringC param_;
229   Location paramLoc_;
230   Char minus_;
231   Char tab_;
232   Char rs_;
233   Char re_;
234   Char space_;
235   StringC publicKey_;
236   StringC systemKey_;
237   StringC entityKey_;
238   StringC doctypeKey_;
239   StringC linktypeKey_;
240   StringC notationKey_;
241   StringC overrideKey_;
242   StringC sgmlDeclKey_;
243   StringC documentKey_;
244   StringC catalogKey_;
245   StringC yesKey_;
246   StringC noKey_;
247   StringC baseKey_;
248   StringC delegateKey_;
249   XcharMap<unsigned char> categoryTable_;
250   SubstTable<Char> substTable_;
251   Boolean override_;
252 };
253
254 ExtendEntityManager::CatalogManager *
255 SOCatalogManager::make(const Vector<StringC> &sysids,
256                        size_t nSysidsMustExist,
257                        const CharsetInfo &sysidCharset,
258                        const CharsetInfo &catalogCharset)
259 {
260   return new SOCatalogManagerImpl(sysids,
261                                   nSysidsMustExist,
262                                   sysidCharset,
263                                   catalogCharset);
264 }
265                        
266 SOCatalogManagerImpl::SOCatalogManagerImpl(const Vector<StringC> &systemCatalogs,
267                                            size_t nSystemCatalogsMustExist,
268                                            const CharsetInfo &sysidCharset,
269                                            const CharsetInfo &catalogCharset)
270 : systemCatalogs_(systemCatalogs),
271   nSystemCatalogsMustExist_(nSystemCatalogsMustExist),
272   sysidCharset_(sysidCharset),
273   catalogCharset_(catalogCharset)
274 {
275 }
276
277 Boolean SOCatalogManagerImpl::mapCatalog(ParsedSystemId &systemId,
278                                          ExtendEntityManager *em,
279                                          Messenger &mgr) const
280 {
281   Vector<ParsedSystemIdMap> maps;
282   systemId.maps.swap(maps);
283   while (maps.size() > 0) {
284     StringC catalogSystemId;
285     systemId.unparse(sysidCharset_, catalogSystemId);
286     SOEntityCatalog *catalog = new SOEntityCatalog(em);
287     ConstPtr<EntityCatalog> deleter(catalog);
288     CatalogParser parser(catalogCharset_);
289     parser.parseCatalog(catalogSystemId, 1, sysidCharset_, catalogCharset_,
290                         new InputSourceOrigin, catalog, mgr);
291     // FIXME do catalog caching here
292     StringC s;
293     if (maps.back().type == ParsedSystemIdMap::catalogDocument) {
294       if (!catalog->document(sysidCharset_, mgr, s)) {
295         mgr.message(CatalogMessages::noDocumentEntry,
296                     StringMessageArg(catalogSystemId));
297         return 0;
298       }
299     }
300     else {
301       ASSERT(maps.back().type == ParsedSystemIdMap::catalogPublic);
302       if (!catalog->lookupPublic(maps.back().publicId, sysidCharset_, mgr,
303                                  s)) {
304         mgr.message(CatalogMessages::noPublicEntry,
305                     StringMessageArg(maps.back().publicId),
306                     StringMessageArg(catalogSystemId));
307         return 0;
308       }
309     }
310     ParsedSystemId tem;
311     if (!em->parseSystemId(s, sysidCharset_, 0, 0, mgr, tem))
312       return 0;
313     systemId = tem;
314     maps.resize(maps.size() - 1);
315     for (size_t i = 0; i < systemId.maps.size(); i++)
316       maps.push_back(systemId.maps[i]);
317     systemId.maps.clear();
318   }
319   return 1;
320 }
321
322 ConstPtr<EntityCatalog>
323 SOCatalogManagerImpl::makeCatalog(StringC &systemId,
324                                   const CharsetInfo &charset,
325                                   ExtendEntityManager *em,
326                                   Messenger &mgr) const
327 {
328   SOEntityCatalog *entityCatalog = new SOEntityCatalog(em);
329   CatalogParser parser(catalogCharset_);
330   size_t i;
331   for (i = 0; i < nSystemCatalogsMustExist_; i++)
332     parser.parseCatalog(systemCatalogs_[i], 1,
333                         sysidCharset_, catalogCharset_,
334                         new InputSourceOrigin, entityCatalog,
335                         mgr);
336   addCatalogsForDocument(parser, systemId, entityCatalog, charset, mgr);
337   for (i = nSystemCatalogsMustExist_; i < systemCatalogs_.size(); i++)
338     parser.parseCatalog(systemCatalogs_[i], 0,
339                         sysidCharset_, catalogCharset_,
340                         new InputSourceOrigin, entityCatalog,
341                         mgr);
342
343   return entityCatalog;
344 }
345
346
347 void SOCatalogManagerImpl::addCatalogsForDocument(CatalogParser &parser,
348                                                   StringC &sysid,
349                                                   SOEntityCatalog *impl,
350                                                   const CharsetInfo &charset,
351                                                   Messenger &mgr) const
352 {
353   ParsedSystemId v;
354   if (!impl->entityManager()->parseSystemId(sysid, charset, 0, 0, mgr, v))
355     return;
356   if (v.maps.size() > 0) {
357     if (v.maps[0].type == ParsedSystemIdMap::catalogDocument) {
358       v.maps.erase(v.maps.begin(), v.maps.begin() + 1);
359       StringC tem;
360       v.unparse(charset, tem);
361       parser.parseCatalog(tem, 1, charset, catalogCharset_,
362                           new InputSourceOrigin, impl, mgr);
363       if (!impl->document(charset, mgr, sysid)) {
364         mgr.message(CatalogMessages::noDocumentEntry, StringMessageArg(tem));
365         sysid.resize(0);
366       }
367     }
368     return;
369   }
370   Vector<StringC> catalogs;
371   size_t i;
372   for (i = 0; i < v.size(); i++)
373     if (v[i].storageManager->inheritable()) {
374       ParsedSystemId catalogId;
375       catalogId.resize(1);
376       StorageObjectSpec &spec = catalogId.back();
377       spec.storageManager = v[i].storageManager;
378       spec.codingSystemName = v[i].codingSystemName;
379       spec.specId = spec.storageManager->idCharset()->execToDesc("catalog");
380       spec.storageManager->resolveRelative(v[i].specId, spec.specId, 0);
381       spec.baseId = v[i].baseId;
382       spec.records = v[i].records;
383       StringC tem;
384       catalogId.unparse(charset, tem);
385       for (size_t j = 0; j < catalogs.size(); j++)
386         if (tem == catalogs[j]) {
387           tem.resize(0);
388           break;
389         }
390       if (tem.size() > 0) {
391         catalogs.resize(catalogs.size() + 1);
392         tem.swap(catalogs.back());
393       }
394     }
395   for (i = 0; i < catalogs.size(); i++)
396     parser.parseCatalog(catalogs[i], 0, charset,
397                         catalogCharset_, new InputSourceOrigin, impl,
398                         mgr);
399 }
400
401 SOEntityCatalog::SOEntityCatalog(Ptr<ExtendEntityManager> em)
402 : em_(em), catalogNumber_(0), haveSgmlDecl_(0), haveDocument_(0),
403   haveCurrentBase_(0)
404 {
405 }
406
407 void SOEntityCatalog::endCatalog()
408 {
409   catalogNumber_++;
410   haveCurrentBase_ = 0;
411 }
412
413 Boolean SOEntityCatalog::expandCatalogSystemId(const StringC &str,
414                                                const Location &loc,
415                                                size_t baseNumber,
416                                                Boolean isNdata,
417                                                const CharsetInfo &charset,
418                                                const StringC *lookupPublicId,
419                                                Messenger &mgr,
420                                                StringC &result) const
421 {
422   return em_->expandSystemId(str,
423                              (baseNumber ? base_[baseNumber - 1] : loc),
424                              isNdata,
425                              charset,
426                              lookupPublicId,
427                              mgr,
428                              result);
429 }
430
431 Boolean SOEntityCatalog::lookup(const EntityDecl &entity,
432                                 const Syntax &syntax,
433                                 const CharsetInfo &charset,
434                                 Messenger &mgr,
435                                 StringC &result) const
436 {
437   const CatalogEntry *entry = 0;
438   const CatalogEntry *delegatedEntry = 0;
439   if (entity.systemIdPointer())
440     entry = systemIds_.lookup(*entity.systemIdPointer());
441   if (entity.publicIdPointer()) {
442     const CatalogEntry *publicEntry;
443     Boolean delegated;
444     publicEntry = findBestPublicEntry(*entity.publicIdPointer(),
445                                       entity.systemIdPointer() != 0,
446                                       charset,
447                                       delegated);
448     if (publicEntry && delegated)
449       delegatedEntry = publicEntry;
450     // match for system id has priority over match for public id in same
451     // catalog
452     if (publicEntry
453         && (!entry || publicEntry->catalogNumber < entry->catalogNumber))
454       entry = publicEntry;
455   }
456   if (entity.name().size() > 0
457       && (!entry || entry->catalogNumber > 0)) {
458     const CatalogEntry *entityEntry; 
459     int tableIndex = (entity.declType() >= EntityDecl::parameterEntity
460                       ? int(entity.declType()) - 1
461                       : int(entity.declType()));
462     StringC name(entity.name());
463     Boolean subst;
464     switch (entity.declType()) {
465     case EntityDecl::parameterEntity:
466       {
467         StringC tem(name);
468         name = syntax.peroDelim();
469         name += tem;
470       }
471       // fall through
472     case EntityDecl::generalEntity:
473       subst = syntax.namecaseEntity();
474       break;
475     default:
476       subst = syntax.namecaseGeneral();
477       break;
478     }
479     if (!subst)
480       entityEntry = names_[tableIndex].lookup(name,
481                                               entity.systemIdPointer() != 0);
482     else
483       entityEntry = names_[tableIndex].lookup(entity.name(),
484                                               syntax.upperSubstTable(),
485                                               entity.systemIdPointer() != 0);
486     // match for public id has priority over match for entity in same
487     // catalog
488     if (entityEntry
489         && (!entry || entityEntry->catalogNumber < entry->catalogNumber))
490       entry = entityEntry;
491   }
492   if (entry)
493     return expandCatalogSystemId(entry->to,
494                                  entry->loc,
495                                  entry->baseNumber,
496                                  entity.dataType() == EntityDecl::ndata,
497                                  charset,
498                                  entry == delegatedEntry
499                                  ? entity.publicIdPointer()
500                                  : 0,
501                                  mgr,
502                                  result);
503   if (entity.systemIdPointer())
504     return em_->expandSystemId(*entity.systemIdPointer(),
505                                entity.defLocation(),
506                                entity.dataType() == EntityDecl::ndata,
507                                charset,
508                                0,
509                                mgr,
510                                result);
511   return 0;
512 }
513
514 Boolean SOEntityCatalog::lookupPublic(const StringC &publicId,
515                                       const CharsetInfo &charset,
516                                       Messenger &mgr,
517                                       StringC &result) const
518 {
519   Boolean delegated;
520   const CatalogEntry *entry = findBestPublicEntry(publicId, 0, charset,
521                                                   delegated);
522   return (entry
523           && expandCatalogSystemId(entry->to, entry->loc, entry->baseNumber,
524                                    0, charset, delegated ? &publicId : 0,
525                                    mgr, result));
526                                  
527 }
528
529 const CatalogEntry *
530 SOEntityCatalog::findBestPublicEntry(const StringC &publicId,
531                                      Boolean overrideOnly,
532                                      const CharsetInfo &charset,
533                                      Boolean &delegated) const
534 {
535   Char slash = charset.execToDesc('/');
536   Char colon = charset.execToDesc(':');
537   const CatalogEntry *bestEntry = 0;
538   for (size_t i = 0; i <= publicId.size(); i++) {
539     if ((i + 1 < publicId.size()
540          && (publicId[i] == slash || publicId[i] == colon)
541          && publicId[i + 1] == publicId[i])
542         || (i >= 2
543             && (publicId[i - 1] == slash || publicId[i - 1] == colon)
544             && publicId[i - 2] == publicId[i - 1])) {
545       StringC tem(publicId.data(), i);
546       const CatalogEntry *entry = delegates_.lookup(tem, overrideOnly);
547       if (entry
548           && (!bestEntry
549               || entry->catalogNumber <= bestEntry->catalogNumber)) {
550         bestEntry = entry;
551         delegated = 1;
552       }
553     }
554   }
555   const CatalogEntry *entry = publicIds_.lookup(publicId, overrideOnly);
556   if (entry
557       && (!bestEntry || entry->catalogNumber <= bestEntry->catalogNumber)) {
558     bestEntry = entry;
559     delegated = 0;
560   }
561   return bestEntry;
562 }
563
564 Boolean SOEntityCatalog::sgmlDecl(const CharsetInfo &charset,
565                                   Messenger &mgr,
566                                   StringC &result) const
567
568 {
569   return haveSgmlDecl_ && expandCatalogSystemId(sgmlDecl_, sgmlDeclLoc_,
570                                                 sgmlDeclBaseNumber_,
571                                                 0, charset, 0, mgr, result);
572 }
573
574 Boolean SOEntityCatalog::document(const CharsetInfo &charset,
575                                   Messenger &mgr,
576                                   StringC &result) const
577
578 {
579   return haveDocument_ && expandCatalogSystemId(document_, documentLoc_,
580                                                 documentBaseNumber_,
581                                                 0, charset, 0, mgr, result);
582 }
583
584 Boolean SOEntityCatalog::defaultDoctype(const CharsetInfo &charset,
585                                         Messenger &mgr,
586                                         StringC &name,
587                                         StringC &sysid) const
588 {
589   if (defaultDoctype_.size() == 0)
590     return 0;
591   int tableIndex = EntityDecl::doctype;
592   if (tableIndex >= EntityDecl::parameterEntity)
593     tableIndex--;
594   const CatalogEntry *entry
595     = names_[tableIndex].lookup(defaultDoctype_, 0);
596   name = defaultDoctype_;
597   return expandCatalogSystemId(entry->to,
598                                entry->loc,
599                                entry->baseNumber,
600                                0,
601                                charset,
602                                0,
603                                mgr,
604                                sysid);
605 }
606
607 void SOEntityCatalog::addPublicId(StringC &publicId, StringC &systemId,
608                                   const Location &loc, Boolean override)
609 {
610   CatalogEntry entry;
611   entry.loc = loc;
612   entry.catalogNumber = catalogNumber_;
613   entry.baseNumber = haveCurrentBase_ ? base_.size() : 0;
614   systemId.swap(entry.to);
615   publicIds_.insert(publicId, entry, override);
616 }
617
618 void SOEntityCatalog::addDelegate(StringC &prefix, StringC &systemId,
619                                   const Location &loc, Boolean override)
620 {
621   CatalogEntry entry;
622   entry.loc = loc;
623   entry.catalogNumber = catalogNumber_;
624   entry.baseNumber = haveCurrentBase_ ? base_.size() : 0;
625   systemId.swap(entry.to);
626   delegates_.insert(prefix, entry, override);
627 }
628
629 void SOEntityCatalog::addSystemId(StringC &systemId, StringC &toSystemId,
630                                   const Location &loc)
631 {
632   CatalogEntry entry;
633   entry.loc = loc;
634   entry.catalogNumber = catalogNumber_;
635   entry.baseNumber = haveCurrentBase_ ? base_.size() : 0;
636   toSystemId.swap(entry.to);
637   systemIds_.insert(systemId, entry, false);
638 }
639
640 void SOEntityCatalog::addName(StringC &name, DeclType declType,
641                               StringC &systemId, const Location &loc,
642                               Boolean override)
643 {
644   if (declType == EntityDecl::doctype
645       && defaultDoctype_.size() == 0)
646     defaultDoctype_ = name;
647   CatalogEntry entry;
648   entry.loc = loc;
649   entry.catalogNumber = catalogNumber_;
650   entry.baseNumber = haveCurrentBase_ ? base_.size() : 0;
651   int tableIndex = (declType >= EntityDecl::parameterEntity
652                     ? int(declType) - 1
653                     : int(declType));
654   entry.serial = names_[tableIndex].count();
655   systemId.swap(entry.to);
656   names_[tableIndex].insert(name, entry, override);
657 }
658
659 void SOEntityCatalog::setSgmlDecl(StringC &str, const Location &loc)
660 {
661   if (!haveSgmlDecl_) {
662     haveSgmlDecl_ = true;
663     str.swap(sgmlDecl_);
664     sgmlDeclLoc_ = loc;
665     sgmlDeclBaseNumber_ = haveCurrentBase_ ? base_.size() : 0;
666
667   }
668 }
669
670 void SOEntityCatalog::setDocument(StringC &str, const Location &loc)
671 {
672   if (!haveDocument_) {
673     haveDocument_ = true;
674     str.swap(document_);
675     documentLoc_ = loc;
676     documentBaseNumber_ = haveCurrentBase_ ? base_.size() : 0;
677   }
678 }
679
680 void SOEntityCatalog::setBase(const Location &loc)
681 {
682   if (loc.origin().isNull())
683     haveCurrentBase_ = 0;
684   else {
685     haveCurrentBase_ = 1;
686     base_.push_back(loc);
687   }
688 }
689
690 SOEntityCatalog::Table::Table()
691 {
692 }
693
694 void SOEntityCatalog::Table::insert(const StringC &key,
695                                     const CatalogEntry &entry,
696                                     Boolean override)
697 {
698   if (override)
699     overrideEntries_.insert(key, entry, false);
700   else {
701     const CatalogEntry *e = overrideEntries_.lookup(key);
702     if (!e)
703       normalEntries_.insert(key, entry, false);
704   }
705 }
706
707 const CatalogEntry *SOEntityCatalog::Table::lookup(const StringC &key,
708                                                    Boolean overrideOnly) const
709 {
710   if (!overrideOnly) {
711     const CatalogEntry *e = normalEntries_.lookup(key);
712     if (e)
713       return e;
714   }
715   return overrideEntries_.lookup(key);
716 }
717
718 const CatalogEntry *
719 SOEntityCatalog::Table::lookup(const StringC &name,
720                                const SubstTable<Char> &substTable,
721                                Boolean overrideOnly) const
722 {
723   HashTableIter<StringC,CatalogEntry> iter1(overrideEntries_);
724   HashTableIter<StringC,CatalogEntry> iter2(normalEntries_);
725   HashTableIter<StringC,CatalogEntry> *iters[2];
726   int nIter = 0;
727   iters[nIter++] = &iter1;
728   if (!overrideOnly)
729     iters[nIter++] = &iter2;
730   const CatalogEntry *entry = 0;
731   for (int i = 0; i < nIter; i++) {
732     HashTableIter<StringC,CatalogEntry> &iter = *iters[i];
733     const StringC *key;
734     const CatalogEntry *value;
735     StringC buffer;
736     while (iter.next(key, value)) {
737       buffer = *key;
738       for (size_t j = 0; j < buffer.size(); j++)
739         substTable.subst(buffer[j]);
740       if (buffer == name) {
741         if (!entry || value->serial < entry->serial)
742           entry = value;
743       }
744     }
745   }
746   return entry;
747 }
748
749 size_t SOEntityCatalog::Table::count() const
750 {
751   return normalEntries_.count() + overrideEntries_.count();
752 }
753
754 CatalogParser::CatalogParser(const CharsetInfo &charset)
755 : categoryTable_(data),
756   entityKey_(charset.execToDesc("ENTITY")),
757   publicKey_(charset.execToDesc("PUBLIC")),
758   systemKey_(charset.execToDesc("SYSTEM")),
759   doctypeKey_(charset.execToDesc("DOCTYPE")),
760   linktypeKey_(charset.execToDesc("LINKTYPE")),
761   notationKey_(charset.execToDesc("NOTATION")),
762   overrideKey_(charset.execToDesc("OVERRIDE")),
763   sgmlDeclKey_(charset.execToDesc("SGMLDECL")),
764   documentKey_(charset.execToDesc("DOCUMENT")),
765   catalogKey_(charset.execToDesc("CATALOG")),
766   yesKey_(charset.execToDesc("YES")),
767   noKey_(charset.execToDesc("NO")),
768   baseKey_(charset.execToDesc("BASE")),
769   delegateKey_(charset.execToDesc("DELEGATE"))
770 {
771   static const char lcletters[] = "abcdefghijklmnopqrstuvwxyz";
772   static const char ucletters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
773   // minimum data other than lcletter, ucletter
774   static const char minChars[] = "0123456789-.'()+,/:=?";
775   static const char sChars[] = " \n\r\t";
776   categoryTable_.setChar(0, nul);
777   const char *p;
778   const char *q;
779   for (p = lcletters, q = ucletters; *p; p++, q++) {
780     Char lc = charset.execToDesc(*p);
781     Char uc = charset.execToDesc(*q);
782     substTable_.addSubst(lc, uc);
783     categoryTable_.setChar(lc, min);
784     categoryTable_.setChar(uc, min);
785   }
786   for (p = sChars; *p; p++)
787     categoryTable_.setChar(charset.execToDesc(*p), s);
788   for (p = minChars; *p; p++)
789     categoryTable_.setChar(charset.execToDesc(*p), min);
790   categoryTable_.setChar(charset.execToDesc('\''), lita);
791   categoryTable_.setChar(charset.execToDesc('"'), lit);
792   minus_ = charset.execToDesc('-');
793   categoryTable_.setChar(minus_, minus);
794   tab_ = charset.execToDesc('\t');
795   re_ = charset.execToDesc('\r');
796   rs_ = charset.execToDesc('\n');
797   space_ = charset.execToDesc(' ');
798   categoryTable_.setEe(eof);
799 }
800
801 void CatalogParser::parseCatalog(const StringC &sysid,
802                                  Boolean mustExist,
803                                  const CharsetInfo &sysidCharset,
804                                  const CharsetInfo &catalogCharset,
805                                  InputSourceOrigin *origin,
806                                  SOEntityCatalog *catalog,
807                                  Messenger &mgr)
808 {
809   const Ptr<ExtendEntityManager> &em = catalog->entityManager();
810   in_ = (mustExist
811          ? em->open(sysid, sysidCharset, origin, 0, mgr)
812          : em->openIfExists(sysid, sysidCharset, origin, 0, mgr));
813   if (!in_)
814     return;
815   catalog_ = catalog;
816   mgr_ = &mgr;
817   override_ = 0;
818   Boolean recovering = false;
819   Vector<StringC> subSysids;
820   Vector<Location> subSysidLocs;
821   for (;;) {
822     Param parm = parseParam();
823     if (parm == nameParam) {
824       upcase(param_);
825       recovering = false;
826       if (param_ == publicKey_)
827         parsePublic();
828       else if (param_ == systemKey_)
829         parseSystem();
830       else if (param_ == entityKey_)
831         parseNameMap(EntityDecl::generalEntity);
832       else if (param_ == doctypeKey_)
833         parseNameMap(EntityDecl::doctype);
834       else if (param_ == linktypeKey_)
835         parseNameMap(EntityDecl::linktype);
836       else if (param_ == notationKey_)
837         parseNameMap(EntityDecl::notation);
838       else if (param_ == sgmlDeclKey_) {
839         if (parseArg())
840           catalog_->setSgmlDecl(param_, paramLoc_);
841       }
842       else if (param_ == documentKey_) {
843         if (parseArg())
844           catalog_->setDocument(param_, paramLoc_);
845       }
846       else if (param_ == overrideKey_)
847         parseOverride();
848       else if (param_ == catalogKey_) {
849         if (parseArg()) {
850           if (inLoop(paramLoc_))
851             break;
852           subSysids.resize(subSysids.size() + 1);
853           param_.swap(subSysids.back());
854           subSysidLocs.push_back(paramLoc_);
855         }
856       }
857       else if (param_ == baseKey_) {
858         if (parseArg()) {
859           StringC tem;
860           if (em->expandSystemId(param_,
861                                  paramLoc_,
862                                  0,
863                                  catalogCharset,
864                                  0,
865                                  mgr,
866                                  tem)) {
867             InputSource *in = em->open(tem,
868                                        catalogCharset,
869                                        new InputSourceOrigin(paramLoc_),
870                                        0,
871                                        mgr);
872             if (in)
873               catalog->setBase(in->currentLocation());
874           }
875         }
876       }
877       else if (param_ == delegateKey_)
878         parseDelegate();
879       else {
880         if (parseParam() == eofParam)
881           break;
882         recovering = true;
883       }
884     }
885     else if (parm == eofParam)
886       break;
887     else if (!recovering) {
888       recovering = true;
889       message(CatalogMessages::nameExpected);
890     }
891   }
892   delete in_;
893   catalog->endCatalog();
894   for (size_t i = 0; i < subSysids.size(); i++) {
895     StringC tem;
896     if (em->expandSystemId(subSysids[i], subSysidLocs[i], 0, catalogCharset,
897                            0, mgr, tem))
898       parseCatalog(tem, 1, catalogCharset, catalogCharset,
899                    new InputSourceOrigin(subSysidLocs[i]), catalog, mgr);
900   }
901 }
902
903 Boolean CatalogParser::inLoop(const Location &loc)
904 {
905   const InputSourceOrigin *origin = paramLoc_.origin()->asInputSourceOrigin();
906   if (!origin)
907     return 0;
908   const ExternalInfo *info = origin->externalInfo();
909   if (!info)
910     return 0;
911   StorageObjectLocation soLoc;
912   if (!ExtendEntityManager::externalize(info,
913                                         origin->startOffset(paramLoc_.index()),
914                                         soLoc))
915     return 0;
916   for (;;) {
917     const Location &parent = origin->parent();
918     if (parent.origin().isNull())
919       break;
920     origin = parent.origin()->asInputSourceOrigin();
921     if (!origin)
922       break;
923     const ExternalInfo *info1 = origin->externalInfo();
924     if (info1) {
925       StorageObjectLocation soLoc1;
926       if (ExtendEntityManager::externalize(info1,
927                                            origin->startOffset(parent.index()),
928                                            soLoc1)) {
929         const StorageObjectSpec *sos = soLoc.storageObjectSpec;
930         const StorageObjectSpec *sos1 = soLoc1.storageObjectSpec;
931         if (sos->storageManager == sos1->storageManager
932             && sos->id == sos1->id) {
933           setNextLocation(loc.origin()->parent());
934           message(CatalogMessages::inLoop);
935           return 1;
936         }
937       }
938     }
939   }
940   return 0;
941 }
942
943 void CatalogParser::parseOverride()
944 {
945   if (parseParam() != nameParam) {
946     message(CatalogMessages::overrideYesOrNo);
947     return;
948   }
949   upcase(param_);
950   if (param_ == yesKey_)
951     override_ = 1;
952   else if (param_ == noKey_)
953     override_ = 0;
954   else
955     message(CatalogMessages::overrideYesOrNo);
956 }
957
958 void CatalogParser::parsePublic()
959 {
960   if (parseParam(minimumLiteral) != literalParam) {
961     message(CatalogMessages::literalExpected);
962     return;
963   }
964   StringC publicId;
965   param_.swap(publicId);
966   if (!parseArg())
967     return;
968   catalog_->addPublicId(publicId, param_, paramLoc_, override_);
969 }
970
971 void CatalogParser::parseDelegate()
972 {
973   if (parseParam(minimumLiteral) != literalParam) {
974     message(CatalogMessages::literalExpected);
975     return;
976   }
977   StringC publicId;
978   param_.swap(publicId);
979   if (!parseArg())
980     return;
981   catalog_->addDelegate(publicId, param_, paramLoc_, override_);
982 }
983
984 void CatalogParser::parseSystem()
985 {
986   if (!parseArg())
987     return;
988   StringC systemId;
989   param_.swap(systemId);
990   Param parm = parseParam();
991   if (parm == nameParam)
992     message(CatalogMessages::systemShouldQuote);
993   else if (parm != literalParam) {
994     message(CatalogMessages::literalExpected);
995     return;
996   }
997   catalog_->addSystemId(systemId, param_, paramLoc_);
998 }
999
1000 void CatalogParser::parseNameMap(EntityDecl::DeclType declType)
1001 {
1002   if (!parseArg())
1003     return;
1004   StringC name;
1005   param_.swap(name);
1006   if (!parseArg())
1007     return;
1008   catalog_->addName(name, declType, param_, paramLoc_, override_);
1009 }
1010
1011 Boolean CatalogParser::parseArg()
1012 {
1013   Param parm = parseParam();
1014   if (parm != nameParam && parm != literalParam) {
1015     message(CatalogMessages::nameOrLiteralExpected);
1016     return false;
1017   }
1018   return true;
1019 }
1020
1021 CatalogParser::Param CatalogParser::parseParam(unsigned flags)
1022 {
1023   for (;;) {
1024     Xchar c = get();
1025     switch (categoryTable_[c]) {
1026     case eof:
1027       return eofParam;
1028     case lit:
1029     case lita:
1030       parseLiteral(c, flags);
1031       return literalParam;
1032     case s:
1033       break;
1034     case nul:
1035       message(CatalogMessages::nulChar);
1036       break;
1037     case minus:
1038       c = get();
1039       if (c == minus_) {
1040         skipComment();
1041         break;
1042       }
1043       unget();
1044       // fall through
1045     default:
1046       parseName();
1047       return nameParam;
1048     }
1049   }
1050 }
1051
1052 void CatalogParser::skipComment()
1053 {
1054   for (;;) {
1055     Xchar c = get();
1056     if (c == minus_) {
1057       c = get();
1058       if (c == minus_)
1059         break;
1060     }
1061     if (c == InputSource::eE) {
1062       message(CatalogMessages::eofInComment);
1063       break;
1064     }
1065   }
1066 }
1067
1068 void CatalogParser::parseLiteral(Char delim, unsigned flags)
1069 {
1070   paramLoc_ = in_->currentLocation();
1071   enum { no, yesBegin, yesMiddle } skipping = yesBegin;
1072   param_.resize(0);
1073   for (;;) {
1074     Xchar c = get();
1075     if (c == InputSource::eE) {
1076       message(CatalogMessages::eofInLiteral);
1077       break;
1078     }
1079     if (Char(c) == delim)
1080       break;
1081     if (flags & minimumLiteral) {
1082       if (!isMinimumData(c))
1083         message(CatalogMessages::minimumData);
1084       if (c == rs_)
1085         ;
1086       else if (c == space_ || c == re_) {
1087         if (skipping == no) {
1088           param_ += space_;
1089           skipping = yesMiddle;
1090         }
1091       }
1092       else {
1093         skipping = no;
1094         param_ += Char(c);
1095       }
1096     }
1097     else
1098       param_ += Char(c);
1099   }
1100   if (skipping == yesMiddle)
1101     param_.resize(param_.size() - 1);
1102 }
1103
1104 void CatalogParser::parseName()
1105 {
1106   paramLoc_ = in_->currentLocation();
1107   size_t length;
1108   for (length = 1;; length++) {
1109     Xchar c = in_->tokenChar(messenger());
1110     int cat = categoryTable_[c];
1111     if (cat == eof || cat == s)
1112       break;
1113     // FIXME maybe check for LIT or LITA
1114     if (cat == nul)
1115       message(CatalogMessages::nulChar);
1116   }
1117   in_->endToken(length);
1118   param_.assign(in_->currentTokenStart(), in_->currentTokenLength());
1119 }
1120
1121 void CatalogParser::upcase(StringC &str)
1122 {
1123   for (size_t i = 0; i < str.size(); i++)
1124     substTable_.subst(str[i]);
1125 }
1126
1127 void CatalogParser::dispatchMessage(const Message &msg)
1128 {
1129   mgr_->dispatchMessage(msg);
1130 }
1131
1132 void CatalogParser::dispatchMessage(Message &msg)
1133 {
1134   mgr_->dispatchMessage(msg);
1135 }
1136
1137 void CatalogParser::initMessage(Message &msg)
1138 {
1139   msg.loc = in_->currentLocation();
1140 }
1141
1142 #ifdef SP_NAMESPACE
1143 }
1144 #endif