rpc.cmsd: use TIRPC on Linux
[oweals/cde.git] / cde / programs / nsgmls / ExtendEntityManager.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 libraries 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: ExtendEntityManager.C /main/1 1996/07/29 16:51:42 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 "ExtendEntityManager.h"
33 #include "Message.h"
34 #include "MessageArg.h"
35 #include "OffsetOrderedList.h"
36 #include "rtti.h"
37 #include "StorageManager.h"
38 #include "Vector.h"
39 #include "NCVector.h"
40 #include "Owner.h"
41 #include "RegisteredCodingSystem.h"
42 #include "constant.h"
43 #include "EntityManagerMessages.h"
44 #include "StorageObjectPosition.h"
45 #include "Owner.h"
46 #include "CodingSystem.h"
47 #include "InputSource.h"
48 #include "macros.h"
49 #include "EntityCatalog.h"
50
51 #include <stddef.h>
52 #include <string.h>
53 #include <stdlib.h>
54 #include <ctype.h>
55 #include <stdio.h>
56
57 #ifdef DECLARE_MEMMOVE
58 extern "C" {
59   void *memmove(void *, const void *, size_t);
60 }
61 #endif
62
63 #ifdef SP_NAMESPACE
64 namespace SP_NAMESPACE {
65 #endif
66
67 const char EOFCHAR = '\032';    // Control-Z
68
69 class ExternalInputSource;
70
71 class EntityManagerImpl : public ExtendEntityManager {
72 public:
73   EntityManagerImpl(StorageManager *defaultStorageManager,
74                     const InputCodingSystem *defaultCodingSystem);
75   void setCatalogManager(CatalogManager *catalogManager);
76   void registerStorageManager(StorageManager *);
77   void registerCodingSystem(const char *, const InputCodingSystem *);
78   InputSource *open(const StringC &sysid,
79                     const CharsetInfo &,
80                     InputSourceOrigin *,
81                     Boolean mayRewind,
82                     Messenger &);
83   InputSource *openIfExists(const StringC &sysid,
84                             const CharsetInfo &,
85                             InputSourceOrigin *,
86                             Boolean mayRewind,
87                             Messenger &);
88   ConstPtr<EntityCatalog> makeCatalog(StringC &systemId,
89                                                   const CharsetInfo &charset,
90                                                   Messenger &mgr);
91   Boolean expandSystemId(const StringC &,
92                          const Location &,
93                          Boolean isNdata,
94                          const CharsetInfo &,
95                          const StringC *,
96                          Messenger &,
97                          StringC &);
98   Boolean mergeSystemIds(const Vector<StringC> &,
99                          Boolean mapCatalogDocument,
100                          const CharsetInfo &,
101                          Messenger &mgr,
102                          StringC &) const;
103   StorageManager *lookupStorageType(const StringC &, const CharsetInfo &) const;
104   StorageManager *lookupStorageType(const char *) const;
105   StorageManager *guessStorageType(const StringC &, const CharsetInfo &) const;
106   const InputCodingSystem *lookupCodingSystem(const StringC &,
107                                               const CharsetInfo &,
108                                               const char *&) const;
109   Boolean resolveSystemId(const StringC &str,
110                           const CharsetInfo &idCharset,
111                           Messenger &mgr,
112                           const Location &defLocation,
113                           Boolean isNdata,
114                           ParsedSystemId &parsedSysid) const;
115   Boolean parseSystemId(const StringC &str,
116                         const CharsetInfo &idCharset,
117                         Boolean isNdata,
118                         const StorageObjectSpec *defSpec,
119                         Messenger &mgr,
120                         ParsedSystemId &parsedSysid) const;
121 private:
122   EntityManagerImpl(const EntityManagerImpl &); // undefined
123   void operator=(const EntityManagerImpl &); // undefined
124   static const StorageObjectSpec *defStorageObject(const Location &);
125   static Boolean matchKey(const StringC &type, const char *s,
126                           const CharsetInfo &docCharset);
127   NCVector<Owner<StorageManager> > storageManagers_;
128   Vector<RegisteredCodingSystem> codingSystems_;
129   Owner<StorageManager> defaultStorageManager_;
130   const InputCodingSystem *defaultCodingSystem_;
131   Owner<CatalogManager> catalogManager_;
132   friend class FSIParser;
133 };
134
135 class ExternalInfoImpl : public ExternalInfo {
136   RTTI_CLASS
137 public:
138   ExternalInfoImpl(ParsedSystemId &parsedSysid);
139   const StorageObjectSpec &spec(size_t i) const;
140   size_t nSpecs() const;
141   const ParsedSystemId &parsedSystemId() const;
142   void noteRS(Offset);
143   void noteStorageObjectEnd(Offset);
144   void noteInsertedRSs();
145   void setDecoder(size_t i, Decoder *);
146   StringC &id(size_t i);
147   Boolean convertOffset(Offset, StorageObjectLocation &) const;
148 private:
149   ParsedSystemId parsedSysid_;
150   NCVector<StorageObjectPosition> position_;
151   size_t currentIndex_;
152   // list of inserted RSs
153   OffsetOrderedList rsList_;
154   Boolean notrack_;
155 };
156
157 class ExternalInputSource : public InputSource {
158 public:
159   ExternalInputSource(ParsedSystemId &parsedSysid,
160                       InputSourceOrigin *origin,
161                       Boolean mayRewind,
162                       Boolean mayNotExist = 0);
163   void pushCharRef(Char, const NamedCharRef &);
164   ~ExternalInputSource();
165 private:
166   Xchar fill(Messenger &);
167   Boolean rewind(Messenger &);
168   void willNotRewind();
169
170   void init();
171   void noteRS();
172   void noteRSAt(const Char *);
173   void reallocateBuffer(size_t size);
174   void insertChar(Char);
175   static const Char *findNextCr(const Char *start, const Char *end);
176   static const Char *findNextLf(const Char *start, const Char *end);
177   static const Char *findNextCrOrLf(const Char *start, const Char *end);
178
179   ExternalInfoImpl *info_;
180   Char *buf_;
181   const Char *bufLim_;
182   Offset bufLimOffset_;
183   size_t bufSize_;
184   size_t readSize_;
185   NCVector<Owner<StorageObject> > sov_;
186   StorageObject *so_;
187   size_t soIndex_;
188   Boolean insertRS_;
189   Decoder *decoder_;
190   const char *leftOver_;
191   size_t nLeftOver_;
192   Boolean mayRewind_;
193   Boolean mayNotExist_;
194   enum RecordType {
195     unknown,
196     crUnknown,
197     crlf,
198     lf,
199     cr,
200     asis
201     };
202   RecordType recordType_;
203   Boolean zapEof_;
204 };
205
206 class FSIParser {
207 public:
208   FSIParser(const StringC &, const CharsetInfo &idCharset,
209             Boolean isNdata,
210             const StorageObjectSpec *defSpec,
211             const EntityManagerImpl *em,
212             Messenger &mgr);
213   Boolean parse(ParsedSystemId &parsedSysid);
214   static const char *recordsName(StorageObjectSpec::Records records);
215   struct RecordType {
216     const char *name;
217     StorageObjectSpec::Records value;
218   };
219 private:
220   Boolean handleInformal(size_t startIndex, ParsedSystemId &parsedSysid);
221   Boolean convertId(StringC &, Xchar smcrd, const StorageManager *);
222   Xchar get();
223   void unget();
224   StorageManager *lookupStorageType(const StringC &key, Boolean &neutral);
225   Boolean matchKey(const StringC &, const char *);
226   Boolean matchChar(Xchar, char);
227   Boolean isS(Xchar);
228   Boolean convertDigit(Xchar c, int &weight);
229   void uncharref(StringC &);
230   Boolean setAttributes(StorageObjectSpec &sos, Boolean neutral,
231                         Xchar &smcrd, Boolean &fold);
232   Boolean setCatalogAttributes(ParsedSystemId &parsedSysid);
233   void setDefaults(StorageObjectSpec &sos);
234   Boolean parseAttribute(StringC &token, Boolean &gotValue, StringC &value);
235   Boolean lookupRecords(const StringC &token, StorageObjectSpec::Records &);
236   void convertMinimumLiteral(const StringC &from, StringC &to);
237
238   const StringC &str_;
239   size_t strIndex_;
240   Messenger &mgr_;
241   const EntityManagerImpl *em_;
242   const StorageObjectSpec *defSpec_;
243   const CharsetInfo &idCharset_;
244   Boolean isNdata_;
245   static RecordType recordTypeTable[];
246 };
247
248 const Char RS = '\n';
249 const Char RE = '\r';
250 const char lineEnd = '\n';
251
252 ExtendEntityManager::CatalogManager::~CatalogManager()
253 {
254 }
255
256 ExtendEntityManager *ExtendEntityManager::make(StorageManager *sm,
257                                                const InputCodingSystem *cs)
258 {
259   return new EntityManagerImpl(sm, cs);
260 }
261
262 Boolean ExtendEntityManager::externalize(const ExternalInfo *info,
263                                          Offset off,
264                                          StorageObjectLocation &loc)
265 {
266   if (!info)
267     return false;
268   const ExternalInfoImpl *p = DYNAMIC_CAST_CONST_PTR(ExternalInfoImpl, info);
269   if (!p)
270     return false;
271   return p->convertOffset(off, loc);
272 }
273
274 const ParsedSystemId *
275 ExtendEntityManager::externalInfoParsedSystemId(const ExternalInfo *info)
276 {
277   if (!info)
278     return 0;
279   const ExternalInfoImpl *p = DYNAMIC_CAST_CONST_PTR(ExternalInfoImpl, info);
280   if (!p)
281     return 0;
282   return &p->parsedSystemId();
283 }
284
285
286 EntityManagerImpl::EntityManagerImpl(StorageManager *defaultStorageManager,
287                                      const InputCodingSystem *defaultCodingSystem)
288 : defaultStorageManager_(defaultStorageManager),
289   defaultCodingSystem_(defaultCodingSystem)
290 {
291 }
292
293 InputSource *EntityManagerImpl::open(const StringC &sysid,
294                                      const CharsetInfo &docCharset,
295                                      InputSourceOrigin *origin,
296                                      Boolean mayRewind,
297                                      Messenger &mgr)
298 {
299   ParsedSystemId parsedSysid;
300   if (!parseSystemId(sysid, docCharset, 0, 0, mgr, parsedSysid)
301       || !catalogManager_->mapCatalog(parsedSysid, this, mgr))
302     return 0;
303   return new ExternalInputSource(parsedSysid, origin, mayRewind, 0);
304 }
305
306 InputSource *EntityManagerImpl::openIfExists(const StringC &sysid,
307                                              const CharsetInfo &docCharset,
308                                              InputSourceOrigin *origin,
309                                              Boolean mayRewind,
310                                              Messenger &mgr)
311 {
312   ParsedSystemId parsedSysid;
313   if (!parseSystemId(sysid, docCharset, 0, 0, mgr, parsedSysid)
314       || !catalogManager_->mapCatalog(parsedSysid, this, mgr))
315     return 0;
316   return new ExternalInputSource(parsedSysid, origin, mayRewind, 1);
317 }
318
319 ConstPtr<EntityCatalog>
320 EntityManagerImpl::makeCatalog(StringC &systemId,
321                                const CharsetInfo &charset,
322                                Messenger &mgr)
323 {
324   return catalogManager_->makeCatalog(systemId, charset, this, mgr);
325 }
326
327 Boolean
328 EntityManagerImpl::mergeSystemIds(const Vector<StringC> &sysids,
329                                   Boolean mapCatalogDocument,
330                                   const CharsetInfo &charset,
331                                   Messenger &mgr,
332                                   StringC &result) const
333 {
334   ParsedSystemId parsedSysid;
335   if (mapCatalogDocument) {
336     parsedSysid.maps.resize(parsedSysid.maps.size() + 1);
337     parsedSysid.maps.back().type = ParsedSystemIdMap::catalogDocument;
338   }
339   for (size_t i = 0; i < sysids.size(); i++)
340     if (!parseSystemId(sysids[i],
341                        charset,
342                        0,
343                        0,
344                        mgr,
345                        parsedSysid))
346       return 0;
347   parsedSysid.unparse(charset, result);
348   return 1;
349 }
350
351 Boolean
352 EntityManagerImpl::expandSystemId(const StringC &str,
353                                   const Location &defLocation,
354                                   Boolean isNdata,
355                                   const CharsetInfo &charset,
356                                   const StringC *mapCatalogPublic,
357                                   Messenger &mgr,
358                                   StringC &result)
359 {
360   ParsedSystemId parsedSysid;
361   const StorageObjectSpec *defSpec = defStorageObject(defLocation);
362   if (!parseSystemId(str, charset, isNdata, defSpec, mgr, parsedSysid))
363     return 0;
364   if (mapCatalogPublic) {
365     ParsedSystemIdMap map;
366     map.type = ParsedSystemIdMap::catalogPublic;
367     map.publicId = *mapCatalogPublic;
368     parsedSysid.maps.insert(parsedSysid.maps.begin(), 1, map);
369   }
370   parsedSysid.unparse(charset, result);
371   return 1;
372 }
373
374 Boolean EntityManagerImpl::parseSystemId(const StringC &str,
375                                          const CharsetInfo &idCharset,
376                                          Boolean isNdata,
377                                          const StorageObjectSpec *defSpec,
378                                          Messenger &mgr,
379                                          ParsedSystemId &parsedSysid) const
380 {
381   FSIParser fsiParser(str, idCharset, isNdata, defSpec, this, mgr);
382   return fsiParser.parse(parsedSysid);
383 }
384
385 StorageManager *
386 EntityManagerImpl::guessStorageType(const StringC &type,
387                                     const CharsetInfo &docCharset) const
388 {
389   for (size_t i = 0; i < storageManagers_.size(); i++)
390     if (storageManagers_[i]->guessIsId(type, docCharset))
391       return storageManagers_[i].pointer();
392   if (defaultStorageManager_->guessIsId(type, docCharset))
393     return defaultStorageManager_.pointer();
394   return 0;
395 }
396
397 StorageManager *
398 EntityManagerImpl::lookupStorageType(const StringC &type,
399                                      const CharsetInfo &docCharset) const
400 {
401   if (type.size() == 0)
402     return 0;
403   if (matchKey(type, defaultStorageManager_->type(), docCharset))
404     return defaultStorageManager_.pointer();
405   for (size_t i = 0; i < storageManagers_.size(); i++)
406     if (matchKey(type, storageManagers_[i]->type(), docCharset))
407       return storageManagers_[i].pointer();
408   return 0;
409 }
410
411 StorageManager *
412 EntityManagerImpl::lookupStorageType(const char *type) const
413 {
414   if (type == defaultStorageManager_->type())
415     return defaultStorageManager_.pointer();
416   for (size_t i = 0; i < storageManagers_.size(); i++)
417     if (type == storageManagers_[i]->type())
418       return storageManagers_[i].pointer();
419   return 0;
420 }
421
422 const InputCodingSystem *
423 EntityManagerImpl::lookupCodingSystem(const StringC &type,
424                                       const CharsetInfo &docCharset,
425                                       const char *&name) const
426 {
427   for (size_t i = 0; i < codingSystems_.size(); i++)
428     if (matchKey(type, codingSystems_[i].name, docCharset)) {
429       name = codingSystems_[i].name;
430       return codingSystems_[i].ics;
431     }
432   return 0;
433 }
434
435 Boolean
436 EntityManagerImpl::matchKey(const StringC &type,
437                             const char *s,
438                             const CharsetInfo &docCharset)
439 {
440   if (strlen(s) != type.size())
441     return false;
442   for (size_t i = 0; i < type.size(); i++)
443     if (docCharset.execToDesc(toupper(s[i])) != type[i]
444         && docCharset.execToDesc(tolower(s[i])) != type[i])
445       return false;
446   return true;
447 }
448
449 void EntityManagerImpl::registerStorageManager(StorageManager *sm)
450 {
451   storageManagers_.resize(storageManagers_.size() + 1);
452   storageManagers_.back() = sm;
453 }
454
455 void EntityManagerImpl::registerCodingSystem(const char *name,
456                                              const InputCodingSystem *ics)
457 {
458   codingSystems_.resize(codingSystems_.size() + 1);
459   RegisteredCodingSystem &rcs = codingSystems_.back();
460   rcs.name = name;
461   rcs.ics = ics;
462 }
463
464 void EntityManagerImpl::setCatalogManager(CatalogManager *catalogManager)
465 {
466   catalogManager_ = catalogManager;
467 }
468
469 const StorageObjectSpec *
470 EntityManagerImpl::defStorageObject(const Location &defLocation)
471 {
472   Offset off;
473   const ExternalInfo *info;
474   Location loc(defLocation);
475   for (;;) {
476     if (loc.origin().isNull())
477       return 0;
478     const InputSourceOrigin *inputSourceOrigin = loc.origin()->asInputSourceOrigin();
479     if (inputSourceOrigin) {
480       off = inputSourceOrigin->startOffset(loc.index());
481       info = inputSourceOrigin->externalInfo();
482       if (info)
483         break;
484       if (!inputSourceOrigin->defLocation(off, loc))
485         return 0;
486     }
487     else
488       loc = loc.origin()->parent();
489   }
490   StorageObjectLocation soLoc;
491   if (!ExtendEntityManager::externalize(info, off, soLoc))
492     return 0;
493   return soLoc.storageObjectSpec;
494 }
495
496 ExternalInputSource::ExternalInputSource(ParsedSystemId &parsedSysid,
497                                          InputSourceOrigin *origin,
498                                          Boolean mayRewind,
499                                          Boolean mayNotExist)
500 : InputSource(origin, 0, 0),
501   mayRewind_(mayRewind),
502   mayNotExist_(mayNotExist),
503   sov_(parsedSysid.size()),
504   readSize_(0),
505   decoder_(NULL),
506   recordType_(unknown),
507   zapEof_(false)
508 {
509   init();
510   info_ = new ExternalInfoImpl(parsedSysid);
511   origin->setExternalInfo(info_);
512 }
513
514 void ExternalInputSource::init()
515 {
516   so_ = 0;
517   buf_ = 0;
518   bufSize_ = 0;
519   bufLim_ = 0;
520   bufLimOffset_ = 0;
521   insertRS_ = true;
522   soIndex_ = 0;
523   leftOver_ = 0;
524   nLeftOver_ = 0;  
525 }
526
527 ExternalInputSource::~ExternalInputSource()
528 {
529   if (buf_)
530     delete [] buf_;
531 }
532
533 Boolean ExternalInputSource::rewind(Messenger &mgr)
534 {
535   reset(0, 0);
536   if (buf_)
537     delete [] buf_;
538   // reset makes a new EntityOrigin
539   ParsedSystemId parsedSysid(info_->parsedSystemId());
540   info_ = new ExternalInfoImpl(parsedSysid);
541   inputSourceOrigin()->setExternalInfo(info_);
542   so_ = 0;
543   for (size_t i = 0; i < soIndex_; i++) {
544     if (sov_[i] && !sov_[i]->rewind(mgr))
545       return 0;
546   }
547   init();
548   return 1;
549 }
550
551 void ExternalInputSource::willNotRewind()
552 {
553   for (size_t i = 0; i < soIndex_; i++)
554     if (sov_[i])
555       sov_[i]->willNotRewind();
556   mayRewind_ = 0;
557 }
558
559 // Round up N so that it is a power of TO.
560 // TO must be a power of 2.
561
562 inline
563 size_t roundUp(size_t n, size_t to)
564 {
565   return (n + (to - 1)) & ~(to - 1);
566 }
567
568 inline
569 void ExternalInputSource::noteRSAt(const Char *p)
570 {
571   info_->noteRS(bufLimOffset_ - (bufLim_ - p));
572 }
573
574 inline
575 void ExternalInputSource::noteRS()
576 {
577   noteRSAt(cur());
578 }
579
580 Xchar ExternalInputSource::fill(Messenger &mgr)
581 {
582   ASSERT(cur() == end());
583   while (end() >= bufLim_) {
584     // need more data
585     while (so_ == 0) {
586       if (soIndex_ >= sov_.size())
587         return eE;
588       if (soIndex_ > 0)
589         info_->noteStorageObjectEnd(bufLimOffset_ - (bufLim_ - end()));
590       const StorageObjectSpec &spec = info_->spec(soIndex_);
591       if (mayNotExist_) {
592         NullMessenger nullMgr;
593         sov_[soIndex_]
594           = spec.storageManager->makeStorageObject(spec.specId, spec.baseId,
595                                                    spec.search,
596                                                    mayRewind_, nullMgr,
597                                                    info_->id(soIndex_));
598       }
599       else
600         sov_[soIndex_]
601           = spec.storageManager->makeStorageObject(spec.specId, spec.baseId,
602                                                    spec.search,
603                                                    mayRewind_, mgr,
604                                                    info_->id(soIndex_));
605       so_ = sov_[soIndex_].pointer();
606       if (so_) {
607         decoder_ = spec.codingSystem->makeDecoder();
608         info_->setDecoder(soIndex_, decoder_);
609         zapEof_ = spec.zapEof;
610         switch (spec.records) {
611         case StorageObjectSpec::asis:
612           recordType_ = asis;
613           insertRS_ = false;
614           break;
615         case StorageObjectSpec::cr:
616           recordType_ = cr;
617           break;
618         case StorageObjectSpec::lf:
619           recordType_ = lf;
620           break;
621         case StorageObjectSpec::crlf:
622           recordType_ = crlf;
623           break;
624         case StorageObjectSpec::find:
625           recordType_ = unknown;
626           break;
627         default:
628           CANNOT_HAPPEN();
629         }
630         soIndex_++;
631         readSize_ = so_->getBlockSize();
632         nLeftOver_ = 0;
633         break;
634       }
635       else
636         setAccessError();
637       soIndex_++;
638     }
639
640     size_t keepSize = end() - start();
641     const size_t align = sizeof(int)/sizeof(Char);
642     size_t readSizeChars = (readSize_ + (sizeof(Char) - 1))/sizeof(Char);
643     readSizeChars = roundUp(readSizeChars, align);
644     size_t neededSize;          // in Chars
645     size_t startOffset;
646     // compute neededSize and readSize
647     unsigned minBytesPerChar = decoder_->minBytesPerChar();
648     if (nLeftOver_ == 0 && minBytesPerChar >= sizeof(Char)) {
649       // In this case we want to do decoding in place.
650       // FIXME It might be a win on some systems (Irix?) to arrange that the
651       // read buffer is on a page boundary.
652
653       if (keepSize >= size_t(-1)/sizeof(Char) - (align - 1) - insertRS_)
654         abort();                        // FIXME throw an exception
655       
656       // Now size_t(-1)/sizeof(Char) - (align - 1) - insertRS_ - keepSize > 0
657       if (readSizeChars
658           > size_t(-1)/sizeof(Char) - (align - 1) - insertRS_ - keepSize)
659         abort();
660       neededSize = roundUp(readSizeChars + keepSize + insertRS_, align);
661       startOffset = ((neededSize > bufSize_ ? neededSize : bufSize_)
662                      - readSizeChars - insertRS_ - keepSize);
663     }
664     else {
665       // Needs to be room for everything before decoding.
666       neededSize = (keepSize + insertRS_ + readSizeChars
667                     + (nLeftOver_ + sizeof(Char) - 1)/sizeof(Char));
668       // Also must be room for everything after decoding.
669       size_t neededSize2
670         = (keepSize + insertRS_
671            // all the converted characters
672            + (nLeftOver_ + readSize_)/minBytesPerChar
673            // enough Chars to contain left over bytes
674            + ((readSize_ % minBytesPerChar + sizeof(Char) - 1)
675               / sizeof(Char)));
676       if (neededSize2 > neededSize)
677         neededSize = neededSize2;
678       neededSize = roundUp(neededSize, align);
679       if (neededSize > size_t(-1)/sizeof(Char))
680         abort();
681       startOffset = 0;
682     }
683     if (bufSize_ < neededSize)
684       reallocateBuffer(neededSize);
685     Char *newStart = buf_ + startOffset;
686     if (newStart != start() && keepSize > 0)
687       memmove(newStart, start(), keepSize*sizeof(Char));
688     char *bytesStart = (char *)(buf_ + bufSize_ - readSizeChars) - nLeftOver_;
689     if (nLeftOver_ > 0 && leftOver_ != bytesStart)
690       memmove(bytesStart, leftOver_, nLeftOver_);
691     moveStart(newStart);
692     bufLim_ = end();
693
694     size_t nread;
695     if (so_->read((char *)(buf_ + bufSize_ - readSizeChars), readSize_,
696                   mgr, nread)) {
697       if (nread > 0) {
698         const char *bytesEnd = bytesStart + nLeftOver_ + nread;
699         size_t nChars = decoder_->decode((Char *)end() + insertRS_,
700                                          bytesStart,
701                                          nLeftOver_ + nread
702                                          - (zapEof_ && bytesEnd[-1] == EOFCHAR),
703                                          &leftOver_);
704         nLeftOver_ = bytesEnd - leftOver_;
705         if (nChars > 0) {
706           if (insertRS_) {
707             noteRS();
708             *(Char *)end() = RS;
709             advanceEnd(end() + 1);
710             insertRS_ = false;
711             bufLim_ += 1;
712             bufLimOffset_ += 1;
713           }
714           bufLim_ += nChars;
715           bufLimOffset_ += nChars;
716           break;
717         }
718       }
719     }
720     else
721       so_ = 0;
722   }
723   ASSERT(end() < bufLim_);
724   if (insertRS_) {
725     noteRS();
726     insertChar(RS);
727     insertRS_ = false;
728     bufLimOffset_ += 1;
729   }
730   switch (recordType_) {
731   case unknown:
732     {
733       const Char *e = findNextCrOrLf(end(), bufLim_);
734       if (e) {
735         if (*e == '\n') {
736           recordType_ = lf;
737           info_->noteInsertedRSs();
738           *(Char *)e = RE;
739           advanceEnd(e + 1);
740           insertRS_ = true;
741         }
742         else {
743           if (e + 1 < bufLim_) {
744             if (e[1] == '\n') {
745               recordType_ = crlf;
746               advanceEnd(e + 1);
747               if (e + 2 == bufLim_) {
748                 bufLim_--;
749                 bufLimOffset_--;
750                 insertRS_ = true;
751               }
752             }
753             else {
754               advanceEnd(e + 1);
755               recordType_ = cr;
756               info_->noteInsertedRSs();
757               insertRS_ = true;
758             }
759           }
760           else {
761             recordType_ = crUnknown;
762             advanceEnd(e + 1);
763           }
764         }
765       }
766       else
767         advanceEnd(bufLim_);
768     }
769     break;
770   case crUnknown:
771     {
772       if (*cur() == '\n') {
773         noteRS();
774         advanceEnd(cur() + 1);
775         recordType_ = crlf;
776       }
777       else {
778         advanceEnd(cur() + 1);
779         insertRS_ = true;
780         recordType_ = cr;
781         info_->noteInsertedRSs();
782       }
783     }
784     break;
785   case lf:
786     {
787       Char *e = (Char *)findNextLf(end(), bufLim_);
788       if (e) {
789         advanceEnd(e + 1);
790         *e = RE;
791         insertRS_ = true;
792       }
793       else
794         advanceEnd(bufLim_);
795     }
796     break;
797   case cr:
798     {
799       const Char *e = findNextCr(end(), bufLim_);
800       if (e) {
801         advanceEnd(e + 1);
802         insertRS_ = true;
803       }
804       else
805         advanceEnd(bufLim_);
806     }
807     break;
808   case crlf:
809     {
810       const Char *e = end();
811       for (;;) {
812         e = findNextLf(e, bufLim_);
813         if (!e) {
814           advanceEnd(bufLim_);
815           break;
816         }
817         // Need to delete final RS if not followed by anything.
818         if (e + 1 == bufLim_) {
819           bufLim_--;
820           bufLimOffset_--;
821           advanceEnd(e);
822           insertRS_ = true;
823           break;
824         }
825         noteRSAt(e);
826         e++;
827       }
828     }
829     break;
830   case asis:
831     advanceEnd(bufLim_);
832     break;
833   default:
834     CANNOT_HAPPEN();
835   }
836
837   return nextChar();
838 }
839
840 const Char *ExternalInputSource::findNextCr(const Char *start,
841                                             const Char *end)
842 {
843   for (; start < end; start++)
844     if (*start == '\r')
845       return start;
846   return 0;
847 }
848
849 const Char *ExternalInputSource::findNextLf(const Char *start,
850                                             const Char *end)
851 {
852   for (; start < end; start++)
853     if (*start == '\n')
854       return start;
855   return 0;
856 }
857
858 const Char *ExternalInputSource::findNextCrOrLf(const Char *start,
859                                                 const Char *end)
860 {
861   for (; start < end; start++)
862     if (*start == '\n' || *start == '\r')
863       return start;
864   return 0;
865 }
866
867 void ExternalInputSource::pushCharRef(Char ch, const NamedCharRef &ref)
868 {
869   ASSERT(cur() == start());
870   noteCharRef(startIndex() + (cur() - start()), ref);
871   insertChar(ch);
872 }
873
874 void ExternalInputSource::insertChar(Char ch)
875 {
876   if (start() > buf_) {
877     if (cur() > start())
878       memmove((Char *)start() - 1, start(), (cur() - start())*sizeof(Char));
879     moveLeft();
880     *(Char *)cur() = ch;
881   }
882   else {
883     // must have start == buf
884     if (buf_ + (bufSize_ - (nLeftOver_ + sizeof(Char) - 1)/sizeof(Char))
885         == bufLim_) {
886       if (bufSize_ == size_t(-1))
887         abort();                // FIXME throw an exception
888       reallocateBuffer(bufSize_ + 1);
889     }
890     else if (nLeftOver_ > 0 && ((char *)(bufLim_ + 1) > leftOver_)) {
891       char *s = (char *)(buf_ + bufSize_) - nLeftOver_;
892       memmove(s, leftOver_, nLeftOver_);
893       leftOver_ = s;
894     }
895     if (cur() < bufLim_)
896       memmove((Char *)cur() + 1, cur(), (bufLim_ - cur())*sizeof(Char));
897     *(Char *)cur() = ch;
898     advanceEnd(end() + 1);
899     bufLim_ += 1;
900   }
901 }
902
903 void ExternalInputSource::reallocateBuffer(size_t newSize)
904 {
905   Char *newBuf = new Char[newSize];
906   
907   memcpy(newBuf, buf_, bufSize_*sizeof(Char));
908   bufSize_ = newSize;
909   changeBuffer(newBuf, buf_);
910   bufLim_ = newBuf + (bufLim_ - buf_);
911   if (nLeftOver_ > 0) {
912     char *s = (char *)(newBuf + bufSize_) - nLeftOver_;
913     memmove(s,
914             newBuf + (leftOver_ - (char *)buf_),
915             nLeftOver_);
916     leftOver_ = s;
917   }
918   delete [] buf_;
919   buf_ = newBuf;
920 }
921
922 RTTI_DEF1(ExternalInfoImpl, ExternalInfo)
923
924 ExternalInfoImpl::ExternalInfoImpl(ParsedSystemId &parsedSysid)
925 : currentIndex_(0), position_(parsedSysid.size())
926 {
927   parsedSysid.swap(parsedSysid_);
928   if (parsedSysid_.size() > 0) {
929     notrack_ = parsedSysid_[0].notrack;
930   } else {
931     notrack_ = false;
932   }
933 }
934
935 StringC &ExternalInfoImpl::id(size_t i)
936 {
937   return parsedSysid_[i].id;
938 }
939
940 void ExternalInfoImpl::setDecoder(size_t i, Decoder *decoder)
941 {
942   position_[i].decoder = decoder;
943 }
944
945 void ExternalInfoImpl::noteInsertedRSs()
946 {
947   position_[currentIndex_].insertedRSs = 1;
948 }
949
950 void ExternalInfoImpl::noteRS(Offset offset)
951 {
952   if (!notrack_)
953     rsList_.append(offset);
954   if (offset
955       == (currentIndex_ == 0 ? 0 : position_[currentIndex_- 1].endOffset))
956     position_[currentIndex_].startsWithRS = 1;
957 }
958
959 void ExternalInfoImpl::noteStorageObjectEnd(Offset offset)
960 {
961   ASSERT(currentIndex_ < position_.size());
962   // The last endOffset_ must be -1.
963   if (currentIndex_ < position_.size() - 1) {
964     position_[currentIndex_++].endOffset = offset;
965     position_[currentIndex_].line1RS = rsList_.size();
966     notrack_ = parsedSysid_[currentIndex_].notrack;
967   }
968 }
969
970 Boolean ExternalInfoImpl::convertOffset(Offset off,
971                                         StorageObjectLocation &ret) const
972 {
973   ret.storageObjectSpec = 0;
974   if (off == Offset(-1) || position_.size() == 0)
975     return false;
976   // the last endOffset_ is Offset(-1), so this will
977   // terminate
978   int i;
979   for (i = 0; off >= position_[i].endOffset; i++)
980     ;
981   for (; parsedSysid_[i].id.size() == 0; i--)
982     if (i == 0)
983       return false;
984   ret.storageObjectSpec = &parsedSysid_[i];
985   Offset startOffset = i == 0 ? 0 : position_[i - 1].endOffset;
986   ret.storageObjectOffset = off - startOffset;
987   ret.byteIndex = ret.storageObjectOffset;
988   if (parsedSysid_[i].notrack
989       || parsedSysid_[i].records == StorageObjectSpec::asis) {
990     ret.lineNumber = (unsigned long)-1;
991     if (parsedSysid_[i].records != StorageObjectSpec::asis) {
992       if (position_[i].insertedRSs)
993         ret.byteIndex = (unsigned long)-1;
994       else if (ret.byteIndex > 0 && position_[i].startsWithRS)
995         ret.byteIndex--;        // first RS is inserted
996     }
997     ret.columnNumber = (unsigned long)-1;
998     return true;
999   }
1000   else {
1001     size_t line1RS = position_[i].line1RS;
1002     // line1RS is now the number of RSs that are before or on the current line.
1003     size_t j;
1004     Offset colStart;
1005     if (rsList_.findPreceding(off, j, colStart)) {
1006       if (position_[i].insertedRSs)
1007         ret.byteIndex -= j + 1 - line1RS;
1008       else if (ret.byteIndex > 0 && position_[i].startsWithRS)
1009         ret.byteIndex--;        // first RS is inserted
1010       j++;
1011       colStart++;
1012     }
1013     else {
1014       j = 0;
1015       colStart = 0;
1016     }
1017     // j is now the number of RSs that are before or on the current line
1018     // colStart is the offset of the first column
1019     ret.lineNumber = j - line1RS + 1 - position_[i].startsWithRS;
1020     // the offset of the first column
1021     if (colStart < startOffset)
1022       colStart = startOffset;
1023     // the RS that starts a line will be in column 0;
1024     // the first real character of a line will be column 1
1025     ret.columnNumber = 1 + off - colStart;
1026   }
1027   if (!position_[i].decoder
1028       || !position_[i].decoder->convertOffset(ret.byteIndex))
1029     ret.byteIndex = (unsigned long)-1;
1030   return true;
1031 }
1032
1033 const StorageObjectSpec &ExternalInfoImpl::spec(size_t i) const
1034 {
1035   return parsedSysid_[i];
1036 }
1037
1038 size_t ExternalInfoImpl::nSpecs() const
1039 {
1040   return parsedSysid_.size();
1041 }
1042
1043 const ParsedSystemId &ExternalInfoImpl::parsedSystemId() const
1044 {
1045   return parsedSysid_;
1046 }
1047
1048 StorageObjectSpec::StorageObjectSpec()
1049 : storageManager(0), codingSystem(0), codingSystemName(0), notrack(0),
1050   records(find), zapEof(1), search(1)
1051 {
1052 }
1053
1054 StorageObjectPosition::StorageObjectPosition()
1055 : endOffset(Offset(-1)), line1RS(0), startsWithRS(0), insertedRSs(0)
1056 {
1057 }
1058
1059 FSIParser::FSIParser(const StringC &str,
1060                      const CharsetInfo &idCharset,
1061                      Boolean isNdata,
1062                      const StorageObjectSpec *defSpec,
1063                      const EntityManagerImpl *em,
1064                      Messenger &mgr)
1065 : str_(str),
1066   strIndex_(0),
1067   idCharset_(idCharset),
1068   isNdata_(isNdata),
1069   defSpec_(defSpec),
1070   em_(em),
1071   mgr_(mgr)
1072 {
1073 }
1074
1075 Xchar FSIParser::get()
1076 {
1077   if (strIndex_ < str_.size())
1078     return str_[strIndex_++];
1079   else
1080     return -1;
1081 }
1082
1083 void FSIParser::unget()
1084 {
1085   if (strIndex_ > 0)
1086     strIndex_ -= 1;
1087 }
1088
1089 Boolean FSIParser::matchKey(const StringC &str, const char *s)
1090 {
1091   if (strlen(s) != str.size())
1092     return false;
1093   for (size_t i = 0; i < str.size(); i++)
1094     if (idCharset_.execToDesc(toupper(s[i])) != str[i]
1095         && idCharset_.execToDesc(tolower(s[i])) != str[i])
1096       return false;
1097   return true;
1098 }
1099
1100 Boolean FSIParser::matchChar(Xchar ch, char execC)
1101 {
1102   return ch == idCharset_.execToDesc(execC);
1103 }
1104
1105 Boolean FSIParser::isS(Xchar c)
1106 {
1107   return (matchChar(c, ' ')
1108           || matchChar(c, '\r')
1109           || matchChar(c, '\n')
1110           || matchChar(c, ' '));
1111 }
1112
1113 Boolean FSIParser::convertDigit(Xchar c, int &weight)
1114 {
1115   static const char digits[] = "0123456789";
1116   for (int i = 0; digits[i] != '\0'; i++)
1117     if (matchChar(c, digits[i])) {
1118       weight = i;
1119       return 1;
1120     }
1121   return 0;
1122 }
1123
1124 Boolean FSIParser::parse(ParsedSystemId &parsedSysid)
1125 {
1126   size_t startIndex = strIndex_;
1127   if (!matchChar(get(), '<'))
1128     return handleInformal(startIndex, parsedSysid);
1129   StringC key;
1130   for (;;) {
1131     Xchar c = get();
1132     if (c == -1)
1133       return handleInformal(startIndex, parsedSysid);
1134     if (isS(c) || matchChar(c, '>'))
1135       break;
1136     key += Char(c);
1137   }
1138   unget();
1139   if (matchKey(key, "CATALOG")) {
1140     if (!setCatalogAttributes(parsedSysid))
1141       return 0;
1142     return parse(parsedSysid);
1143   }
1144   Boolean neutral;
1145   StorageManager *sm = lookupStorageType(key, neutral);
1146   if (!sm)
1147     return handleInformal(startIndex, parsedSysid);
1148   for (;;) {
1149     parsedSysid.resize(parsedSysid.size() + 1);
1150     StorageObjectSpec &sos = parsedSysid.back();
1151     sos.storageManager = sm;
1152     Xchar smcrd;
1153     Boolean fold;
1154     if (!setAttributes(sos, neutral, smcrd, fold))
1155       return 0;
1156     sm = 0;
1157     StringC id;
1158     Boolean hadData = 0;
1159     for (;;) {
1160       Xchar c = get();
1161       if (c == -1)
1162         break;
1163       if (matchChar(c, '<')) {
1164         hadData = 1;
1165         Char stago = c;
1166         key.resize(0);
1167         for (;;) {
1168           c = get();
1169           if (c == -1) {
1170             id += stago;
1171             id += key;
1172             break;
1173           }
1174           if (isS(c) || matchChar(c, '>')) {
1175             unget();
1176             sm = lookupStorageType(key, neutral);
1177             if (!sm) {
1178               id += stago;
1179               id += key;
1180             }
1181             break;
1182           }
1183           key += c;
1184         }
1185         if (sm)
1186           break;
1187       }
1188       else if (!((!hadData && matchChar(c, '\r')) // ignored RE
1189                  || matchChar(c, '\n') )) {       // ignored RS
1190         hadData = 1;
1191         id += c;
1192       }
1193     }
1194     if (id.size() > 0 && matchChar(id[id.size() - 1], '\r'))
1195       id.resize(id.size() - 1);
1196     uncharref(id);
1197     id.swap(sos.specId);
1198     if (!convertId(sos.specId, smcrd, sos.storageManager))
1199       return 0;
1200     if (neutral) {
1201       if (!sos.storageManager->transformNeutral(sos.specId, fold, mgr_))
1202         return 0;
1203     }
1204     if (sos.storageManager->resolveRelative(sos.baseId, sos.specId,
1205                                             sos.search))
1206       sos.baseId.resize(0);
1207     if (!sm)
1208       break;
1209   }
1210   return 1;
1211 }
1212
1213 Boolean FSIParser::handleInformal(size_t index, ParsedSystemId &parsedSysid)
1214 {
1215   parsedSysid.resize(parsedSysid.size() + 1);
1216   StorageObjectSpec &sos = parsedSysid.back();
1217   sos.specId.assign(str_.data() + index,
1218                     str_.size() - index);
1219   sos.storageManager = em_->guessStorageType(sos.specId, idCharset_);
1220   if (!sos.storageManager) {
1221     if (defSpec_ && defSpec_->storageManager->inheritable())
1222       sos.storageManager = defSpec_->storageManager;
1223     else
1224       sos.storageManager = em_->defaultStorageManager_.pointer();
1225   }
1226   setDefaults(sos);
1227   if (!convertId(sos.specId, -1, sos.storageManager))
1228     return 0;
1229   if (sos.storageManager->resolveRelative(sos.baseId, sos.specId, sos.search))
1230     sos.baseId.resize(0);
1231   return 1;
1232 }
1233
1234 StorageManager *FSIParser::lookupStorageType(const StringC &key,
1235                                              Boolean &neutral)
1236 {
1237   if (matchKey(key, "NEUTRAL")) {
1238     neutral = 1;
1239     if (defSpec_ && defSpec_->storageManager->inheritable())
1240       return defSpec_->storageManager;
1241     else
1242       return em_->defaultStorageManager_.pointer();
1243   }
1244   else {
1245     StorageManager *sm = em_->lookupStorageType(key, idCharset_);
1246     if (sm)
1247       neutral = 0;
1248     return sm;
1249   }
1250 }
1251
1252 Boolean FSIParser::setCatalogAttributes(ParsedSystemId &parsedSysid)
1253 {
1254   Boolean hadPublic = 0;
1255   parsedSysid.maps.resize(parsedSysid.maps.size() + 1);
1256   parsedSysid.maps.back().type = ParsedSystemIdMap::catalogDocument;
1257   for (;;) {
1258     StringC token, value;
1259     Boolean gotValue;
1260     if (!parseAttribute(token, gotValue, value)) {
1261       mgr_.message(EntityManagerMessages::fsiSyntax, StringMessageArg(str_));
1262       return 0;
1263     }
1264     if (token.size() == 0)
1265       break;
1266     if (matchKey(token, "PUBLIC")) {
1267       if (hadPublic)
1268         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1269                      StringMessageArg(idCharset_.execToDesc("PUBLIC")));
1270       else if (gotValue) {
1271         convertMinimumLiteral(value, parsedSysid.maps.back().publicId);
1272         parsedSysid.maps.back().type = ParsedSystemIdMap::catalogPublic;
1273       }
1274       else
1275         mgr_.message(EntityManagerMessages::fsiMissingValue,
1276                      StringMessageArg(token));
1277       hadPublic = 1;
1278     }
1279     else
1280       mgr_.message(gotValue
1281                    ? EntityManagerMessages::fsiUnsupportedAttribute
1282                    : EntityManagerMessages::fsiUnsupportedAttributeToken,
1283                    StringMessageArg(token));
1284   }
1285   return 1;
1286 }
1287
1288 void FSIParser::convertMinimumLiteral(const StringC &from, StringC &to)
1289 {
1290   // Do just enough to ensure it can be reparsed.
1291   to.resize(0);
1292   for (size_t i = 0; i < from.size(); i++) {
1293     Char c = from[i];
1294     if (matchChar(c, '"') || matchChar(c, '#'))
1295       mgr_.message(EntityManagerMessages::fsiLookupChar, NumberMessageArg(c));
1296     else if (matchChar(c, ' ')) {
1297       if (to.size() && to[to.size() - 1] != c)
1298         to += c;
1299     }
1300     else
1301       to += c;
1302   }
1303   if (to.size() && matchChar(to[to.size() - 1], ' '))
1304     to.resize(to.size() - 1);
1305 }
1306
1307 // FIXME This should be table driven.
1308
1309 Boolean FSIParser::setAttributes(StorageObjectSpec &sos,
1310                                  Boolean neutral,
1311                                  Xchar &smcrd,
1312                                  Boolean &fold)
1313 {
1314   Boolean hadBctf = 0;
1315   Boolean hadTracking = 0;
1316   Boolean hadSmcrd = 0;
1317   smcrd = -1;
1318   fold = 1;
1319   Boolean hadRecords = 0;
1320   Boolean hadBase = 0;
1321   Boolean hadZapeof = 0;
1322   Boolean hadSearch = 0;
1323   Boolean hadFold = 0;
1324   StorageObjectSpec::Records records;
1325   setDefaults(sos);
1326   for (;;) {
1327     StringC token, value;
1328     Boolean gotValue;
1329     if (!parseAttribute(token, gotValue, value)) {
1330       mgr_.message(EntityManagerMessages::fsiSyntax, StringMessageArg(str_));
1331       return 0;
1332     }
1333     if (token.size() == 0)
1334       break;
1335     if (matchKey(token, "BCTF")) {
1336       if (sos.storageManager->requiredCodingSystem())
1337         mgr_.message(EntityManagerMessages::fsiBctfNotApplicable);
1338       else if (hadBctf)
1339         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1340                      StringMessageArg(token));
1341       else if (gotValue) {
1342         const char *codingSystemName;
1343         const InputCodingSystem *codingSystem
1344           = em_->lookupCodingSystem(value, idCharset_, codingSystemName);
1345         if (codingSystem) {
1346           sos.codingSystem = codingSystem;
1347           sos.codingSystemName = codingSystemName;
1348         }
1349         else if (matchKey(value, "SAME")) {
1350           if (isNdata_) {
1351             if (defSpec_) {
1352               sos.codingSystem = defSpec_->codingSystem;
1353               sos.codingSystemName = defSpec_->codingSystemName;
1354             }
1355             else {
1356               sos.codingSystem = em_->defaultCodingSystem_;
1357               sos.codingSystemName = 0;
1358             }
1359           }
1360         }
1361         else
1362           mgr_.message(EntityManagerMessages::fsiUnknownBctf,
1363                        StringMessageArg(value));
1364       }
1365       else
1366         mgr_.message(EntityManagerMessages::fsiMissingValue,
1367                      StringMessageArg(token));
1368       hadBctf = 1;
1369     }
1370     else if (matchKey(token, "TRACKING")) {
1371       if (hadTracking)
1372         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1373                      StringMessageArg(token));
1374       else if (gotValue) {
1375         if (matchKey(value, "NOTRACK"))
1376           sos.notrack = 1;
1377         else if (!matchKey(value, "TRACK"))
1378           mgr_.message(EntityManagerMessages::fsiBadTracking,
1379                        StringMessageArg(value));
1380       }
1381       else
1382         mgr_.message(EntityManagerMessages::fsiMissingValue,
1383                      StringMessageArg(token));
1384       hadTracking = 1;
1385     }
1386     else if (matchKey(token, "ZAPEOF")) {
1387       if (sos.storageManager->requiredCodingSystem())
1388         mgr_.message(EntityManagerMessages::fsiZapeofNotApplicable);
1389       else if (hadZapeof)
1390         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1391                      StringMessageArg(token));
1392       else if (gotValue) {
1393         if (matchKey(value, "ZAPEOF"))
1394           sos.zapEof = 1;
1395         else if (matchKey(value, "NOZAPEOF"))
1396           sos.zapEof = 0;
1397         else
1398           mgr_.message(EntityManagerMessages::fsiBadZapeof,
1399                        StringMessageArg(value));
1400       }
1401       else
1402         sos.zapEof = 1;
1403       hadZapeof = 1;
1404     }
1405     else if (matchKey(token, "NOZAPEOF")) {
1406       if (sos.storageManager->requiredCodingSystem())
1407         mgr_.message(EntityManagerMessages::fsiZapeofNotApplicable);
1408       else if (hadZapeof)
1409         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1410                      StringMessageArg(idCharset_.execToDesc("ZAPEOF")));
1411       else if (gotValue)
1412         mgr_.message(EntityManagerMessages::fsiValueAsName,
1413                      StringMessageArg(token));
1414       else
1415         sos.zapEof = 0;
1416       hadZapeof = 1;
1417     }
1418     else if (matchKey(token, "SEARCH")) {
1419       if (hadSearch)
1420         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1421                      StringMessageArg(token));
1422       else if (gotValue) {
1423         if (matchKey(value, "SEARCH"))
1424           sos.search = 1;
1425         else if (matchKey(value, "NOSEARCH"))
1426           sos.search = 0;
1427         else
1428           mgr_.message(EntityManagerMessages::fsiBadSearch,
1429                        StringMessageArg(value));
1430       }
1431       else
1432         sos.search = 1;
1433       hadSearch = 1;
1434     }
1435     else if (matchKey(token, "NOSEARCH")) {
1436       if (hadSearch)
1437         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1438                      StringMessageArg(idCharset_.execToDesc("SEARCH")));
1439       else if (gotValue)
1440         mgr_.message(EntityManagerMessages::fsiValueAsName,
1441                      StringMessageArg(token));
1442       else
1443         sos.search = 0;
1444       hadSearch = 1;
1445     }
1446     else if (matchKey(token, "FOLD")) {
1447       if (!neutral)
1448         mgr_.message(EntityManagerMessages::fsiFoldNotNeutral);
1449       else if (hadFold)
1450         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1451                      StringMessageArg(token));
1452       else if (gotValue) {
1453         if (matchKey(value, "FOLD"))
1454           fold = 1;
1455         else if (matchKey(value, "NOFOLD"))
1456           fold = 0;
1457         else
1458           mgr_.message(EntityManagerMessages::fsiBadFold,
1459                        StringMessageArg(value));
1460       }
1461       else
1462         fold = 1;
1463       hadFold = 1;
1464     }
1465     else if (matchKey(token, "NOFOLD")) {
1466       if (!neutral)
1467         mgr_.message(EntityManagerMessages::fsiFoldNotNeutral);
1468       else if (hadFold)
1469         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1470                      StringMessageArg(idCharset_.execToDesc("FOLD")));
1471       else if (gotValue)
1472         mgr_.message(EntityManagerMessages::fsiValueAsName,
1473                      StringMessageArg(token));
1474       else
1475         fold = 0;
1476       hadFold = 1;
1477     }
1478     else if (matchKey(token, "SMCRD")) {
1479       if (hadSmcrd)
1480         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1481                      StringMessageArg(token));
1482       else if (gotValue) {
1483         if (value.size() == 0)
1484           smcrd = -1;
1485         else if (value.size() == 1)
1486           smcrd = value[0];
1487         else
1488           mgr_.message(EntityManagerMessages::fsiBadSmcrd,
1489                        StringMessageArg(value));
1490       }
1491       else
1492         mgr_.message(EntityManagerMessages::fsiMissingValue,
1493                      StringMessageArg(token));
1494       hadSmcrd = 1;
1495     }
1496     else if (matchKey(token, "RECORDS")) {
1497       if (sos.storageManager->requiresCr())
1498         mgr_.message(EntityManagerMessages::fsiRecordsNotApplicable);
1499       else if (hadRecords)
1500         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1501                      StringMessageArg(token));
1502       else if (gotValue) {
1503         if (!lookupRecords(value, sos.records))
1504           mgr_.message(EntityManagerMessages::fsiUnsupportedRecords,
1505                        StringMessageArg(value));
1506       }
1507       else
1508         mgr_.message(EntityManagerMessages::fsiMissingValue,
1509                      StringMessageArg(token));
1510       hadRecords = 1;
1511     }
1512     else if (matchKey(token, "SOIBASE")) {
1513       if (hadBase)
1514         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1515                      StringMessageArg(token));
1516       else if (gotValue)
1517         value.swap(sos.baseId);
1518       else {
1519         mgr_.message(EntityManagerMessages::fsiMissingValue,
1520                      StringMessageArg(token));
1521         sos.baseId.resize(0);
1522       }
1523       hadBase = 1;
1524     }
1525     else if (lookupRecords(token, records)) {
1526       if (sos.storageManager->requiresCr())
1527         mgr_.message(EntityManagerMessages::fsiRecordsNotApplicable);
1528       else if (hadRecords)
1529         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1530                      StringMessageArg(idCharset_.execToDesc("RECORDS")));
1531       else if (!gotValue)
1532         sos.records = records;
1533       else
1534         mgr_.message(EntityManagerMessages::fsiValueAsName,
1535                      StringMessageArg(token));
1536       hadRecords = 1;
1537     }
1538     else if (matchKey(token, "NOTRACK")) {
1539       if (hadTracking)
1540         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1541                      StringMessageArg(idCharset_.execToDesc("TRACKING")));
1542       else if (!gotValue)
1543         sos.notrack = 1;
1544       else
1545         mgr_.message(EntityManagerMessages::fsiValueAsName,
1546                      StringMessageArg(token));
1547       hadTracking = 1;
1548     }
1549     else if (matchKey(token, "TRACK")) {
1550       if (hadTracking)
1551         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1552                      StringMessageArg(idCharset_.execToDesc("TRACKING")));
1553       else if (gotValue)
1554         mgr_.message(EntityManagerMessages::fsiValueAsName,
1555                      StringMessageArg(token));
1556       hadTracking = 1;
1557     }
1558     else
1559       mgr_.message(gotValue
1560                    ? EntityManagerMessages::fsiUnsupportedAttribute
1561                    : EntityManagerMessages::fsiUnsupportedAttributeToken,
1562                    StringMessageArg(token));
1563   }
1564   if (hadBase && sos.baseId.size() > 0) {
1565     convertId(sos.baseId, smcrd, sos.storageManager);
1566     if (neutral) {
1567       if (!sos.storageManager->transformNeutral(sos.baseId, fold, mgr_))
1568         sos.baseId.resize(0);
1569     }
1570   }
1571   if (!hadZapeof && hadRecords && sos.records == StorageObjectSpec::asis)
1572     sos.zapEof = 0;
1573   return 1;
1574 }
1575
1576 FSIParser::RecordType FSIParser::recordTypeTable[] = {
1577   { "FIND", StorageObjectSpec::find },
1578   { "ASIS", StorageObjectSpec::asis },
1579   { "CR", StorageObjectSpec::cr },
1580   { "LF", StorageObjectSpec::lf },
1581   { "CRLF", StorageObjectSpec::crlf }
1582 };
1583
1584 const char *FSIParser::recordsName(StorageObjectSpec::Records records)
1585 {
1586   for (size_t i = 0; i < SIZEOF(recordTypeTable); i++)
1587     if (records == recordTypeTable[i].value)
1588       return recordTypeTable[i].name;
1589   return 0;
1590 }
1591
1592 Boolean FSIParser::lookupRecords(const StringC &token,
1593                                  StorageObjectSpec::Records &result)
1594 {
1595   for (size_t i = 0; i < SIZEOF(recordTypeTable); i++)
1596     if (matchKey(token, recordTypeTable[i].name)) {
1597       result = recordTypeTable[i].value;
1598       return 1;
1599     }
1600   return 0;
1601 }
1602
1603 void FSIParser::setDefaults(StorageObjectSpec &sos)
1604 {
1605   if (sos.storageManager->requiresCr())
1606     sos.records = StorageObjectSpec::cr;
1607   else if (isNdata_
1608            || (defSpec_ && defSpec_->records == StorageObjectSpec::asis))
1609     sos.records = StorageObjectSpec::asis;
1610   if (isNdata_ || (defSpec_ && !defSpec_->zapEof))
1611     sos.zapEof = 0;
1612   if (defSpec_ && defSpec_->storageManager == sos.storageManager) {
1613     if (defSpec_->id.size())
1614       sos.baseId = defSpec_->id;
1615     else {
1616       sos.baseId = defSpec_->specId;
1617       sos.storageManager->resolveRelative(defSpec_->baseId,
1618                                           sos.baseId,
1619                                           0);
1620     }
1621   }
1622   sos.codingSystem = sos.storageManager->requiredCodingSystem();
1623   if (sos.codingSystem)
1624     sos.zapEof = 0;             // hack
1625   else {
1626     sos.codingSystem = em_->defaultCodingSystem_;
1627     if (isNdata_) {
1628       const InputCodingSystem *id = 0;
1629       size_t j;
1630       for (j = 0; j < em_->codingSystems_.size(); j++)
1631         if (em_->codingSystems_[j].ics->isIdentity()) {
1632           id = em_->codingSystems_[j].ics;
1633           break;
1634         }
1635       if (id && id != em_->defaultCodingSystem_) {
1636         sos.codingSystem = id;
1637         sos.codingSystemName = em_->codingSystems_[j].name;
1638       }
1639     }
1640     else if (defSpec_) {
1641       sos.codingSystem = defSpec_->codingSystem;
1642       sos.codingSystemName = defSpec_->codingSystemName;
1643     }
1644   }
1645 }
1646
1647 Boolean FSIParser::parseAttribute(StringC &token, Boolean &gotValue,
1648                                   StringC &value)
1649 {
1650   Xchar c = get();
1651   while (isS(c))
1652     c = get();
1653   if (c == -1) {
1654     return 0;
1655   }
1656   token.resize(0);
1657   if (matchChar(c, '>'))
1658     return 1;
1659   if (matchChar(c, '"') || matchChar(c, '\'') || matchChar(c, '='))
1660     return 0;
1661   for (;;) {
1662     token += c;
1663     c = get();
1664     if (c == -1)
1665       return 0;
1666     if (isS(c))
1667       break;
1668     if (matchChar(c, '>') || matchChar(c, '='))
1669       break;
1670   }
1671   while (isS(c))
1672     c = get();
1673   if (c == -1)
1674     return 0;
1675   if (!matchChar(c, '=')) {
1676     unget();
1677     gotValue = 0;
1678     return 1;
1679   }
1680   gotValue = 1;
1681   value.resize(0);
1682
1683   c = get();
1684   while (isS(c))
1685     c = get();
1686   if (matchChar(c, '>') || matchChar(c, '='))
1687     return 0;
1688   if (matchChar(c, '"') || matchChar(c, '\'')) {
1689     Char lit = c;
1690     for (;;) {
1691       Xchar c = get();
1692       if (c == lit)
1693         break;
1694       if (c == -1)
1695         return 0;
1696       if (matchChar(c, '\n'))
1697         ;
1698       else if (matchChar(c, '\r') || matchChar(c, '\t'))
1699         value += idCharset_.execToDesc(' ');
1700       else
1701         value += c;
1702     }
1703     uncharref(value);
1704   }
1705   else {
1706     for (;;) {
1707       value += c;
1708       c = get();
1709       if (c == -1)
1710         return 0;
1711       if (isS(c))
1712         break;
1713       if (matchChar(c, '>') || matchChar(c, '=')) {
1714         unget();
1715         break;
1716       }
1717     }
1718   }
1719   return 1;
1720 }
1721
1722 void FSIParser::uncharref(StringC &str)
1723 {
1724   size_t j = 0;
1725   size_t i = 0;
1726   while (i < str.size()) {
1727     int digit;
1728     if (matchChar(str[i], '&')
1729         && i + 2 < str.size()
1730         && matchChar(str[i + 1], '#')
1731         && convertDigit(str[i + 2], digit)) {
1732       unsigned long val = digit;
1733       i += 3;
1734       while (i < str.size() && convertDigit(str[i], digit)) {
1735         val = val*10 + digit;
1736         i++;
1737       }
1738       str[j++] = val;
1739       if (i < str.size() && matchChar(str[i], ';'))
1740         i++;
1741     }
1742     else
1743       str[j++] = str[i++];
1744   }
1745   str.resize(j);
1746 }
1747
1748 Boolean FSIParser::convertId(StringC &id, Xchar smcrd,
1749                              const StorageManager *sm)
1750 {
1751   const CharsetInfo *smCharset = sm->idCharset();
1752   StringC newId;
1753   size_t i = 0;
1754   while (i < id.size()) {
1755     UnivChar univ;
1756     WideChar wide;
1757     ISet<WideChar> wideSet;
1758     int digit;
1759     if (Xchar(id[i]) == smcrd
1760         && i + 1 < id.size()
1761         && convertDigit(id[i + 1], digit)) {
1762       i += 2;
1763       Char val = digit;
1764       while (i < id.size() && convertDigit(id[i], digit)) {
1765         val = val*10 + digit;
1766         i++;
1767       }
1768       newId += val;
1769       if (i < id.size() && matchChar(id[i], ';'))
1770         i++;
1771     }
1772     else if (smCharset) {
1773       if (!idCharset_.descToUniv(id[i++], univ))
1774         return 0;
1775       if (univ == UnivCharsetDesc::rs)
1776         ;
1777       else if (univ == UnivCharsetDesc::re && sm->reString())
1778         newId += *sm->reString();
1779       else if (smCharset->univToDesc(univ, wide, wideSet) != 1
1780                || wide > charMax)
1781         return 0;                       // FIXME give error
1782       else
1783         newId += Char(wide);
1784     }
1785     else
1786       newId += id[i++];
1787   }
1788   newId.swap(id);
1789   return 1;
1790 }
1791
1792 ParsedSystemId:: ParsedSystemId()
1793 {
1794 }
1795
1796 static
1797 void unparseSoi(const StringC &soi,
1798                 const CharsetInfo *idCharset,
1799                 const CharsetInfo &resultCharset,
1800                 StringC &result,
1801                 Boolean &needSmcrd);
1802
1803 void ParsedSystemId::unparse(const CharsetInfo &resultCharset,
1804                              StringC &result) const
1805 {
1806   size_t len = size();
1807   result.resize(0);
1808   size_t i;
1809   for (i = 0; i < maps.size(); i++) {
1810     if (maps[i].type == ParsedSystemIdMap::catalogDocument)
1811       result += resultCharset.execToDesc("<CATALOG>");
1812     else if (maps[i].type == ParsedSystemIdMap::catalogPublic) {
1813       result += resultCharset.execToDesc("<CATALOG PUBLIC=\"");
1814       result += maps[i].publicId;
1815       result += resultCharset.execToDesc("\">");
1816     }
1817   }
1818   for (i = 0; i < len; i++) {
1819     const StorageObjectSpec &sos = (*this)[i];
1820     result += resultCharset.execToDesc('<');
1821     result += resultCharset.execToDesc(sos.storageManager->type());
1822     if (sos.notrack)
1823       result += resultCharset.execToDesc(" NOTRACK");
1824     if (!sos.search)
1825       result += resultCharset.execToDesc(" NOSEARCH");
1826     if (!sos.storageManager->requiresCr()
1827         && sos.records != StorageObjectSpec::find) {
1828       result += resultCharset.execToDesc(' ');
1829       result += resultCharset.execToDesc(FSIParser::recordsName(sos.records));
1830     }
1831     if (sos.codingSystemName) {
1832       if (!sos.zapEof)
1833         result += resultCharset.execToDesc(" NOZAPEOF");
1834       result += resultCharset.execToDesc(" BCTF=");
1835       result += resultCharset.execToDesc(sos.codingSystemName);
1836     }
1837     Boolean needSmcrd = 0;
1838     if (sos.baseId.size() != 0) {
1839       result += resultCharset.execToDesc(" SOIBASE='");
1840       unparseSoi(sos.baseId,
1841                  sos.storageManager->idCharset(),
1842                  resultCharset,
1843                  result,
1844                  needSmcrd);
1845       result += resultCharset.execToDesc('\'');
1846     }
1847     StringC tem;
1848     unparseSoi(sos.specId,
1849                sos.storageManager->idCharset(),
1850                resultCharset,
1851                tem,
1852                needSmcrd);
1853     if (needSmcrd)
1854       result += resultCharset.execToDesc(" SMCRD='^'");
1855     result += resultCharset.execToDesc('>');
1856     result += tem;
1857   }
1858 }
1859
1860 void unparseSoi(const StringC &soi,
1861                 const CharsetInfo *idCharset,
1862                 const CharsetInfo &resultCharset,
1863                 StringC &result,
1864                 Boolean &needSmcrd)
1865 {
1866   if (!idCharset) {
1867     for (size_t i = 0; i < soi.size(); i++) {
1868       char buf[32];
1869       sprintf(buf, "&#%lu;", (unsigned long)soi[i]);
1870       result += resultCharset.execToDesc(buf);
1871     }
1872     return;
1873   }
1874   for (size_t i = 0; i < soi.size(); i++) {
1875     UnivChar univ;
1876     WideChar to;
1877     ISet<WideChar> toSet;
1878     if (!idCharset->descToUniv(soi[i], univ)
1879         || univ >= 127
1880         || univ < 32
1881         || univ == 36           // $
1882         || univ == 96           // `
1883 #ifndef MSDOS_FILENAMES
1884         || univ == 92           // backslash
1885 #endif
1886         || univ == 94           // ^
1887         || resultCharset.univToDesc(univ, to, toSet) != 1) {
1888       needSmcrd = 1;
1889       char buf[32];
1890       sprintf(buf, "^%lu;", (unsigned long)soi[i]);
1891       result += resultCharset.execToDesc(buf);
1892     }
1893     else {
1894       switch (univ) {
1895       case 34:          // double quote
1896       case 35:          // #
1897       case 39:          // apostrophe
1898       case 60:          // <
1899         {
1900           char buf[32];
1901           sprintf(buf, "&#%lu;", (unsigned long)to);
1902           result += resultCharset.execToDesc(buf);
1903         }
1904         break;
1905       default:
1906         result += Char(to);
1907         break;
1908       }
1909     }
1910   }
1911 }
1912
1913 #ifdef SP_NAMESPACE
1914 }
1915 #endif