Link with C++ linker
[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 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: 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 {
505   init();
506   info_ = new ExternalInfoImpl(parsedSysid);
507   origin->setExternalInfo(info_);
508 }
509
510 void ExternalInputSource::init()
511 {
512   so_ = 0;
513   buf_ = 0;
514   bufSize_ = 0;
515   bufLim_ = 0;
516   bufLimOffset_ = 0;
517   insertRS_ = true;
518   soIndex_ = 0;
519   leftOver_ = 0;
520   nLeftOver_ = 0;  
521 }
522
523 ExternalInputSource::~ExternalInputSource()
524 {
525   if (buf_)
526     delete [] buf_;
527 }
528
529 Boolean ExternalInputSource::rewind(Messenger &mgr)
530 {
531   reset(0, 0);
532   if (buf_)
533     delete [] buf_;
534   // reset makes a new EntityOrigin
535   ParsedSystemId parsedSysid(info_->parsedSystemId());
536   info_ = new ExternalInfoImpl(parsedSysid);
537   inputSourceOrigin()->setExternalInfo(info_);
538   so_ = 0;
539   for (size_t i = 0; i < soIndex_; i++) {
540     if (sov_[i] && !sov_[i]->rewind(mgr))
541       return 0;
542   }
543   init();
544   return 1;
545 }
546
547 void ExternalInputSource::willNotRewind()
548 {
549   for (size_t i = 0; i < soIndex_; i++)
550     if (sov_[i])
551       sov_[i]->willNotRewind();
552   mayRewind_ = 0;
553 }
554
555 // Round up N so that it is a power of TO.
556 // TO must be a power of 2.
557
558 inline
559 size_t roundUp(size_t n, size_t to)
560 {
561   return (n + (to - 1)) & ~(to - 1);
562 }
563
564 inline
565 void ExternalInputSource::noteRSAt(const Char *p)
566 {
567   info_->noteRS(bufLimOffset_ - (bufLim_ - p));
568 }
569
570 inline
571 void ExternalInputSource::noteRS()
572 {
573   noteRSAt(cur());
574 }
575
576 Xchar ExternalInputSource::fill(Messenger &mgr)
577 {
578   ASSERT(cur() == end());
579   while (end() >= bufLim_) {
580     // need more data
581     while (so_ == 0) {
582       if (soIndex_ >= sov_.size())
583         return eE;
584       if (soIndex_ > 0)
585         info_->noteStorageObjectEnd(bufLimOffset_ - (bufLim_ - end()));
586       const StorageObjectSpec &spec = info_->spec(soIndex_);
587       if (mayNotExist_) {
588         NullMessenger nullMgr;
589         sov_[soIndex_]
590           = spec.storageManager->makeStorageObject(spec.specId, spec.baseId,
591                                                    spec.search,
592                                                    mayRewind_, nullMgr,
593                                                    info_->id(soIndex_));
594       }
595       else
596         sov_[soIndex_]
597           = spec.storageManager->makeStorageObject(spec.specId, spec.baseId,
598                                                    spec.search,
599                                                    mayRewind_, mgr,
600                                                    info_->id(soIndex_));
601       so_ = sov_[soIndex_].pointer();
602       if (so_) {
603         decoder_ = spec.codingSystem->makeDecoder();
604         info_->setDecoder(soIndex_, decoder_);
605         zapEof_ = spec.zapEof;
606         switch (spec.records) {
607         case StorageObjectSpec::asis:
608           recordType_ = asis;
609           insertRS_ = false;
610           break;
611         case StorageObjectSpec::cr:
612           recordType_ = cr;
613           break;
614         case StorageObjectSpec::lf:
615           recordType_ = lf;
616           break;
617         case StorageObjectSpec::crlf:
618           recordType_ = crlf;
619           break;
620         case StorageObjectSpec::find:
621           recordType_ = unknown;
622           break;
623         default:
624           CANNOT_HAPPEN();
625         }
626         soIndex_++;
627         readSize_ = so_->getBlockSize();
628         nLeftOver_ = 0;
629         break;
630       }
631       else
632         setAccessError();
633       soIndex_++;
634     }
635
636     size_t keepSize = end() - start();
637     const size_t align = sizeof(int)/sizeof(Char);
638     size_t readSizeChars = (readSize_ + (sizeof(Char) - 1))/sizeof(Char);
639     readSizeChars = roundUp(readSizeChars, align);
640     size_t neededSize;          // in Chars
641     size_t startOffset;
642     // compute neededSize and readSize
643     unsigned minBytesPerChar = decoder_->minBytesPerChar();
644     if (nLeftOver_ == 0 && minBytesPerChar >= sizeof(Char)) {
645       // In this case we want to do decoding in place.
646       // FIXME It might be a win on some systems (Irix?) to arrange that the
647       // read buffer is on a page boundary.
648
649       if (keepSize >= size_t(-1)/sizeof(Char) - (align - 1) - insertRS_)
650         abort();                        // FIXME throw an exception
651       
652       // Now size_t(-1)/sizeof(Char) - (align - 1) - insertRS_ - keepSize > 0
653       if (readSizeChars
654           > size_t(-1)/sizeof(Char) - (align - 1) - insertRS_ - keepSize)
655         abort();
656       neededSize = roundUp(readSizeChars + keepSize + insertRS_, align);
657       startOffset = ((neededSize > bufSize_ ? neededSize : bufSize_)
658                      - readSizeChars - insertRS_ - keepSize);
659     }
660     else {
661       // Needs to be room for everything before decoding.
662       neededSize = (keepSize + insertRS_ + readSizeChars
663                     + (nLeftOver_ + sizeof(Char) - 1)/sizeof(Char));
664       // Also must be room for everything after decoding.
665       size_t neededSize2
666         = (keepSize + insertRS_
667            // all the converted characters
668            + (nLeftOver_ + readSize_)/minBytesPerChar
669            // enough Chars to contain left over bytes
670            + ((readSize_ % minBytesPerChar + sizeof(Char) - 1)
671               / sizeof(Char)));
672       if (neededSize2 > neededSize)
673         neededSize = neededSize2;
674       neededSize = roundUp(neededSize, align);
675       if (neededSize > size_t(-1)/sizeof(Char))
676         abort();
677       startOffset = 0;
678     }
679     if (bufSize_ < neededSize)
680       reallocateBuffer(neededSize);
681     Char *newStart = buf_ + startOffset;
682     if (newStart != start() && keepSize > 0)
683       memmove(newStart, start(), keepSize*sizeof(Char));
684     char *bytesStart = (char *)(buf_ + bufSize_ - readSizeChars) - nLeftOver_;
685     if (nLeftOver_ > 0 && leftOver_ != bytesStart)
686       memmove(bytesStart, leftOver_, nLeftOver_);
687     moveStart(newStart);
688     bufLim_ = end();
689
690     size_t nread;
691     if (so_->read((char *)(buf_ + bufSize_ - readSizeChars), readSize_,
692                   mgr, nread)) {
693       if (nread > 0) {
694         const char *bytesEnd = bytesStart + nLeftOver_ + nread;
695         size_t nChars = decoder_->decode((Char *)end() + insertRS_,
696                                          bytesStart,
697                                          nLeftOver_ + nread
698                                          - (zapEof_ && bytesEnd[-1] == EOFCHAR),
699                                          &leftOver_);
700         nLeftOver_ = bytesEnd - leftOver_;
701         if (nChars > 0) {
702           if (insertRS_) {
703             noteRS();
704             *(Char *)end() = RS;
705             advanceEnd(end() + 1);
706             insertRS_ = false;
707             bufLim_ += 1;
708             bufLimOffset_ += 1;
709           }
710           bufLim_ += nChars;
711           bufLimOffset_ += nChars;
712           break;
713         }
714       }
715     }
716     else
717       so_ = 0;
718   }
719   ASSERT(end() < bufLim_);
720   if (insertRS_) {
721     noteRS();
722     insertChar(RS);
723     insertRS_ = false;
724     bufLimOffset_ += 1;
725   }
726   switch (recordType_) {
727   case unknown:
728     {
729       const Char *e = findNextCrOrLf(end(), bufLim_);
730       if (e) {
731         if (*e == '\n') {
732           recordType_ = lf;
733           info_->noteInsertedRSs();
734           *(Char *)e = RE;
735           advanceEnd(e + 1);
736           insertRS_ = true;
737         }
738         else {
739           if (e + 1 < bufLim_) {
740             if (e[1] == '\n') {
741               recordType_ = crlf;
742               advanceEnd(e + 1);
743               if (e + 2 == bufLim_) {
744                 bufLim_--;
745                 bufLimOffset_--;
746                 insertRS_ = true;
747               }
748             }
749             else {
750               advanceEnd(e + 1);
751               recordType_ = cr;
752               info_->noteInsertedRSs();
753               insertRS_ = true;
754             }
755           }
756           else {
757             recordType_ = crUnknown;
758             advanceEnd(e + 1);
759           }
760         }
761       }
762       else
763         advanceEnd(bufLim_);
764     }
765     break;
766   case crUnknown:
767     {
768       if (*cur() == '\n') {
769         noteRS();
770         advanceEnd(cur() + 1);
771         recordType_ = crlf;
772       }
773       else {
774         advanceEnd(cur() + 1);
775         insertRS_ = true;
776         recordType_ = cr;
777         info_->noteInsertedRSs();
778       }
779     }
780     break;
781   case lf:
782     {
783       Char *e = (Char *)findNextLf(end(), bufLim_);
784       if (e) {
785         advanceEnd(e + 1);
786         *e = RE;
787         insertRS_ = true;
788       }
789       else
790         advanceEnd(bufLim_);
791     }
792     break;
793   case cr:
794     {
795       const Char *e = findNextCr(end(), bufLim_);
796       if (e) {
797         advanceEnd(e + 1);
798         insertRS_ = true;
799       }
800       else
801         advanceEnd(bufLim_);
802     }
803     break;
804   case crlf:
805     {
806       const Char *e = end();
807       for (;;) {
808         e = findNextLf(e, bufLim_);
809         if (!e) {
810           advanceEnd(bufLim_);
811           break;
812         }
813         // Need to delete final RS if not followed by anything.
814         if (e + 1 == bufLim_) {
815           bufLim_--;
816           bufLimOffset_--;
817           advanceEnd(e);
818           insertRS_ = true;
819           break;
820         }
821         noteRSAt(e);
822         e++;
823       }
824     }
825     break;
826   case asis:
827     advanceEnd(bufLim_);
828     break;
829   default:
830     CANNOT_HAPPEN();
831   }
832
833   return nextChar();
834 }
835
836 const Char *ExternalInputSource::findNextCr(const Char *start,
837                                             const Char *end)
838 {
839   for (; start < end; start++)
840     if (*start == '\r')
841       return start;
842   return 0;
843 }
844
845 const Char *ExternalInputSource::findNextLf(const Char *start,
846                                             const Char *end)
847 {
848   for (; start < end; start++)
849     if (*start == '\n')
850       return start;
851   return 0;
852 }
853
854 const Char *ExternalInputSource::findNextCrOrLf(const Char *start,
855                                                 const Char *end)
856 {
857   for (; start < end; start++)
858     if (*start == '\n' || *start == '\r')
859       return start;
860   return 0;
861 }
862
863 void ExternalInputSource::pushCharRef(Char ch, const NamedCharRef &ref)
864 {
865   ASSERT(cur() == start());
866   noteCharRef(startIndex() + (cur() - start()), ref);
867   insertChar(ch);
868 }
869
870 void ExternalInputSource::insertChar(Char ch)
871 {
872   if (start() > buf_) {
873     if (cur() > start())
874       memmove((Char *)start() - 1, start(), (cur() - start())*sizeof(Char));
875     moveLeft();
876     *(Char *)cur() = ch;
877   }
878   else {
879     // must have start == buf
880     if (buf_ + (bufSize_ - (nLeftOver_ + sizeof(Char) - 1)/sizeof(Char))
881         == bufLim_) {
882       if (bufSize_ == size_t(-1))
883         abort();                // FIXME throw an exception
884       reallocateBuffer(bufSize_ + 1);
885     }
886     else if (nLeftOver_ > 0 && ((char *)(bufLim_ + 1) > leftOver_)) {
887       char *s = (char *)(buf_ + bufSize_) - nLeftOver_;
888       memmove(s, leftOver_, nLeftOver_);
889       leftOver_ = s;
890     }
891     if (cur() < bufLim_)
892       memmove((Char *)cur() + 1, cur(), (bufLim_ - cur())*sizeof(Char));
893     *(Char *)cur() = ch;
894     advanceEnd(end() + 1);
895     bufLim_ += 1;
896   }
897 }
898
899 void ExternalInputSource::reallocateBuffer(size_t newSize)
900 {
901   Char *newBuf = new Char[newSize];
902   
903   memcpy(newBuf, buf_, bufSize_*sizeof(Char));
904   bufSize_ = newSize;
905   changeBuffer(newBuf, buf_);
906   bufLim_ = newBuf + (bufLim_ - buf_);
907   if (nLeftOver_ > 0) {
908     char *s = (char *)(newBuf + bufSize_) - nLeftOver_;
909     memmove(s,
910             newBuf + (leftOver_ - (char *)buf_),
911             nLeftOver_);
912     leftOver_ = s;
913   }
914   delete [] buf_;
915   buf_ = newBuf;
916 }
917
918 RTTI_DEF1(ExternalInfoImpl, ExternalInfo)
919
920 ExternalInfoImpl::ExternalInfoImpl(ParsedSystemId &parsedSysid)
921 : currentIndex_(0), position_(parsedSysid.size())
922 {
923   parsedSysid.swap(parsedSysid_);
924   if (parsedSysid_.size() > 0)
925     notrack_ = parsedSysid_[0].notrack;
926 }
927
928 StringC &ExternalInfoImpl::id(size_t i)
929 {
930   return parsedSysid_[i].id;
931 }
932
933 void ExternalInfoImpl::setDecoder(size_t i, Decoder *decoder)
934 {
935   position_[i].decoder = decoder;
936 }
937
938 void ExternalInfoImpl::noteInsertedRSs()
939 {
940   position_[currentIndex_].insertedRSs = 1;
941 }
942
943 void ExternalInfoImpl::noteRS(Offset offset)
944 {
945   if (!notrack_)
946     rsList_.append(offset);
947   if (offset
948       == (currentIndex_ == 0 ? 0 : position_[currentIndex_- 1].endOffset))
949     position_[currentIndex_].startsWithRS = 1;
950 }
951
952 void ExternalInfoImpl::noteStorageObjectEnd(Offset offset)
953 {
954   ASSERT(currentIndex_ < position_.size());
955   // The last endOffset_ must be -1.
956   if (currentIndex_ < position_.size() - 1) {
957     position_[currentIndex_++].endOffset = offset;
958     position_[currentIndex_].line1RS = rsList_.size();
959     notrack_ = parsedSysid_[currentIndex_].notrack;
960   }
961 }
962
963 Boolean ExternalInfoImpl::convertOffset(Offset off,
964                                         StorageObjectLocation &ret) const
965 {
966   ret.storageObjectSpec = 0;
967   if (off == Offset(-1) || position_.size() == 0)
968     return false;
969   // the last endOffset_ is Offset(-1), so this will
970   // terminate
971   int i;
972   for (i = 0; off >= position_[i].endOffset; i++)
973     ;
974   for (; parsedSysid_[i].id.size() == 0; i--)
975     if (i == 0)
976       return false;
977   ret.storageObjectSpec = &parsedSysid_[i];
978   Offset startOffset = i == 0 ? 0 : position_[i - 1].endOffset;
979   ret.storageObjectOffset = off - startOffset;
980   ret.byteIndex = ret.storageObjectOffset;
981   if (parsedSysid_[i].notrack
982       || parsedSysid_[i].records == StorageObjectSpec::asis) {
983     ret.lineNumber = (unsigned long)-1;
984     if (parsedSysid_[i].records != StorageObjectSpec::asis) {
985       if (position_[i].insertedRSs)
986         ret.byteIndex = (unsigned long)-1;
987       else if (ret.byteIndex > 0 && position_[i].startsWithRS)
988         ret.byteIndex--;        // first RS is inserted
989     }
990     ret.columnNumber = (unsigned long)-1;
991     return true;
992   }
993   else {
994     size_t line1RS = position_[i].line1RS;
995     // line1RS is now the number of RSs that are before or on the current line.
996     size_t j;
997     Offset colStart;
998     if (rsList_.findPreceding(off, j, colStart)) {
999       if (position_[i].insertedRSs)
1000         ret.byteIndex -= j + 1 - line1RS;
1001       else if (ret.byteIndex > 0 && position_[i].startsWithRS)
1002         ret.byteIndex--;        // first RS is inserted
1003       j++;
1004       colStart++;
1005     }
1006     else {
1007       j = 0;
1008       colStart = 0;
1009     }
1010     // j is now the number of RSs that are before or on the current line
1011     // colStart is the offset of the first column
1012     ret.lineNumber = j - line1RS + 1 - position_[i].startsWithRS;
1013     // the offset of the first column
1014     if (colStart < startOffset)
1015       colStart = startOffset;
1016     // the RS that starts a line will be in column 0;
1017     // the first real character of a line will be column 1
1018     ret.columnNumber = 1 + off - colStart;
1019   }
1020   if (!position_[i].decoder
1021       || !position_[i].decoder->convertOffset(ret.byteIndex))
1022     ret.byteIndex = (unsigned long)-1;
1023   return true;
1024 }
1025
1026 const StorageObjectSpec &ExternalInfoImpl::spec(size_t i) const
1027 {
1028   return parsedSysid_[i];
1029 }
1030
1031 size_t ExternalInfoImpl::nSpecs() const
1032 {
1033   return parsedSysid_.size();
1034 }
1035
1036 const ParsedSystemId &ExternalInfoImpl::parsedSystemId() const
1037 {
1038   return parsedSysid_;
1039 }
1040
1041 StorageObjectSpec::StorageObjectSpec()
1042 : storageManager(0), codingSystem(0), codingSystemName(0), notrack(0),
1043   records(find), zapEof(1), search(1)
1044 {
1045 }
1046
1047 StorageObjectPosition::StorageObjectPosition()
1048 : endOffset(Offset(-1)), line1RS(0), startsWithRS(0), insertedRSs(0)
1049 {
1050 }
1051
1052 FSIParser::FSIParser(const StringC &str,
1053                      const CharsetInfo &idCharset,
1054                      Boolean isNdata,
1055                      const StorageObjectSpec *defSpec,
1056                      const EntityManagerImpl *em,
1057                      Messenger &mgr)
1058 : str_(str),
1059   strIndex_(0),
1060   idCharset_(idCharset),
1061   isNdata_(isNdata),
1062   defSpec_(defSpec),
1063   em_(em),
1064   mgr_(mgr)
1065 {
1066 }
1067
1068 Xchar FSIParser::get()
1069 {
1070   if (strIndex_ < str_.size())
1071     return str_[strIndex_++];
1072   else
1073     return -1;
1074 }
1075
1076 void FSIParser::unget()
1077 {
1078   if (strIndex_ > 0)
1079     strIndex_ -= 1;
1080 }
1081
1082 Boolean FSIParser::matchKey(const StringC &str, const char *s)
1083 {
1084   if (strlen(s) != str.size())
1085     return false;
1086   for (size_t i = 0; i < str.size(); i++)
1087     if (idCharset_.execToDesc(toupper(s[i])) != str[i]
1088         && idCharset_.execToDesc(tolower(s[i])) != str[i])
1089       return false;
1090   return true;
1091 }
1092
1093 Boolean FSIParser::matchChar(Xchar ch, char execC)
1094 {
1095   return ch == idCharset_.execToDesc(execC);
1096 }
1097
1098 Boolean FSIParser::isS(Xchar c)
1099 {
1100   return (matchChar(c, ' ')
1101           || matchChar(c, '\r')
1102           || matchChar(c, '\n')
1103           || matchChar(c, ' '));
1104 }
1105
1106 Boolean FSIParser::convertDigit(Xchar c, int &weight)
1107 {
1108   static const char digits[] = "0123456789";
1109   for (int i = 0; digits[i] != '\0'; i++)
1110     if (matchChar(c, digits[i])) {
1111       weight = i;
1112       return 1;
1113     }
1114   return 0;
1115 }
1116
1117 Boolean FSIParser::parse(ParsedSystemId &parsedSysid)
1118 {
1119   size_t startIndex = strIndex_;
1120   if (!matchChar(get(), '<'))
1121     return handleInformal(startIndex, parsedSysid);
1122   StringC key;
1123   for (;;) {
1124     Xchar c = get();
1125     if (c == -1)
1126       return handleInformal(startIndex, parsedSysid);
1127     if (isS(c) || matchChar(c, '>'))
1128       break;
1129     key += Char(c);
1130   }
1131   unget();
1132   if (matchKey(key, "CATALOG")) {
1133     if (!setCatalogAttributes(parsedSysid))
1134       return 0;
1135     return parse(parsedSysid);
1136   }
1137   Boolean neutral;
1138   StorageManager *sm = lookupStorageType(key, neutral);
1139   if (!sm)
1140     return handleInformal(startIndex, parsedSysid);
1141   for (;;) {
1142     parsedSysid.resize(parsedSysid.size() + 1);
1143     StorageObjectSpec &sos = parsedSysid.back();
1144     sos.storageManager = sm;
1145     Xchar smcrd;
1146     Boolean fold;
1147     if (!setAttributes(sos, neutral, smcrd, fold))
1148       return 0;
1149     sm = 0;
1150     StringC id;
1151     Boolean hadData = 0;
1152     for (;;) {
1153       Xchar c = get();
1154       if (c == -1)
1155         break;
1156       if (matchChar(c, '<')) {
1157         hadData = 1;
1158         Char stago = c;
1159         key.resize(0);
1160         for (;;) {
1161           c = get();
1162           if (c == -1) {
1163             id += stago;
1164             id += key;
1165             break;
1166           }
1167           if (isS(c) || matchChar(c, '>')) {
1168             unget();
1169             sm = lookupStorageType(key, neutral);
1170             if (!sm) {
1171               id += stago;
1172               id += key;
1173             }
1174             break;
1175           }
1176           key += c;
1177         }
1178         if (sm)
1179           break;
1180       }
1181       else if (!((!hadData && matchChar(c, '\r')) // ignored RE
1182                  || matchChar(c, '\n') )) {       // ignored RS
1183         hadData = 1;
1184         id += c;
1185       }
1186     }
1187     if (id.size() > 0 && matchChar(id[id.size() - 1], '\r'))
1188       id.resize(id.size() - 1);
1189     uncharref(id);
1190     id.swap(sos.specId);
1191     if (!convertId(sos.specId, smcrd, sos.storageManager))
1192       return 0;
1193     if (neutral) {
1194       if (!sos.storageManager->transformNeutral(sos.specId, fold, mgr_))
1195         return 0;
1196     }
1197     if (sos.storageManager->resolveRelative(sos.baseId, sos.specId,
1198                                             sos.search))
1199       sos.baseId.resize(0);
1200     if (!sm)
1201       break;
1202   }
1203   return 1;
1204 }
1205
1206 Boolean FSIParser::handleInformal(size_t index, ParsedSystemId &parsedSysid)
1207 {
1208   parsedSysid.resize(parsedSysid.size() + 1);
1209   StorageObjectSpec &sos = parsedSysid.back();
1210   sos.specId.assign(str_.data() + index,
1211                     str_.size() - index);
1212   sos.storageManager = em_->guessStorageType(sos.specId, idCharset_);
1213   if (!sos.storageManager) {
1214     if (defSpec_ && defSpec_->storageManager->inheritable())
1215       sos.storageManager = defSpec_->storageManager;
1216     else
1217       sos.storageManager = em_->defaultStorageManager_.pointer();
1218   }
1219   setDefaults(sos);
1220   if (!convertId(sos.specId, -1, sos.storageManager))
1221     return 0;
1222   if (sos.storageManager->resolveRelative(sos.baseId, sos.specId, sos.search))
1223     sos.baseId.resize(0);
1224   return 1;
1225 }
1226
1227 StorageManager *FSIParser::lookupStorageType(const StringC &key,
1228                                              Boolean &neutral)
1229 {
1230   if (matchKey(key, "NEUTRAL")) {
1231     neutral = 1;
1232     if (defSpec_ && defSpec_->storageManager->inheritable())
1233       return defSpec_->storageManager;
1234     else
1235       return em_->defaultStorageManager_.pointer();
1236   }
1237   else {
1238     StorageManager *sm = em_->lookupStorageType(key, idCharset_);
1239     if (sm)
1240       neutral = 0;
1241     return sm;
1242   }
1243 }
1244
1245 Boolean FSIParser::setCatalogAttributes(ParsedSystemId &parsedSysid)
1246 {
1247   Boolean hadPublic = 0;
1248   parsedSysid.maps.resize(parsedSysid.maps.size() + 1);
1249   parsedSysid.maps.back().type = ParsedSystemIdMap::catalogDocument;
1250   for (;;) {
1251     StringC token, value;
1252     Boolean gotValue;
1253     if (!parseAttribute(token, gotValue, value)) {
1254       mgr_.message(EntityManagerMessages::fsiSyntax, StringMessageArg(str_));
1255       return 0;
1256     }
1257     if (token.size() == 0)
1258       break;
1259     if (matchKey(token, "PUBLIC")) {
1260       if (hadPublic)
1261         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1262                      StringMessageArg(idCharset_.execToDesc("PUBLIC")));
1263       else if (gotValue) {
1264         convertMinimumLiteral(value, parsedSysid.maps.back().publicId);
1265         parsedSysid.maps.back().type = ParsedSystemIdMap::catalogPublic;
1266       }
1267       else
1268         mgr_.message(EntityManagerMessages::fsiMissingValue,
1269                      StringMessageArg(token));
1270       hadPublic = 1;
1271     }
1272     else
1273       mgr_.message(gotValue
1274                    ? EntityManagerMessages::fsiUnsupportedAttribute
1275                    : EntityManagerMessages::fsiUnsupportedAttributeToken,
1276                    StringMessageArg(token));
1277   }
1278   return 1;
1279 }
1280
1281 void FSIParser::convertMinimumLiteral(const StringC &from, StringC &to)
1282 {
1283   // Do just enough to ensure it can be reparsed.
1284   to.resize(0);
1285   for (size_t i = 0; i < from.size(); i++) {
1286     Char c = from[i];
1287     if (matchChar(c, '"') || matchChar(c, '#'))
1288       mgr_.message(EntityManagerMessages::fsiLookupChar, NumberMessageArg(c));
1289     else if (matchChar(c, ' ')) {
1290       if (to.size() && to[to.size() - 1] != c)
1291         to += c;
1292     }
1293     else
1294       to += c;
1295   }
1296   if (to.size() && matchChar(to[to.size() - 1], ' '))
1297     to.resize(to.size() - 1);
1298 }
1299
1300 // FIXME This should be table driven.
1301
1302 Boolean FSIParser::setAttributes(StorageObjectSpec &sos,
1303                                  Boolean neutral,
1304                                  Xchar &smcrd,
1305                                  Boolean &fold)
1306 {
1307   Boolean hadBctf = 0;
1308   Boolean hadTracking = 0;
1309   Boolean hadSmcrd = 0;
1310   smcrd = -1;
1311   fold = 1;
1312   Boolean hadRecords = 0;
1313   Boolean hadBase = 0;
1314   Boolean hadZapeof = 0;
1315   Boolean hadSearch = 0;
1316   Boolean hadFold = 0;
1317   StorageObjectSpec::Records records;
1318   setDefaults(sos);
1319   for (;;) {
1320     StringC token, value;
1321     Boolean gotValue;
1322     if (!parseAttribute(token, gotValue, value)) {
1323       mgr_.message(EntityManagerMessages::fsiSyntax, StringMessageArg(str_));
1324       return 0;
1325     }
1326     if (token.size() == 0)
1327       break;
1328     if (matchKey(token, "BCTF")) {
1329       if (sos.storageManager->requiredCodingSystem())
1330         mgr_.message(EntityManagerMessages::fsiBctfNotApplicable);
1331       else if (hadBctf)
1332         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1333                      StringMessageArg(token));
1334       else if (gotValue) {
1335         const char *codingSystemName;
1336         const InputCodingSystem *codingSystem
1337           = em_->lookupCodingSystem(value, idCharset_, codingSystemName);
1338         if (codingSystem) {
1339           sos.codingSystem = codingSystem;
1340           sos.codingSystemName = codingSystemName;
1341         }
1342         else if (matchKey(value, "SAME")) {
1343           if (isNdata_) {
1344             if (defSpec_) {
1345               sos.codingSystem = defSpec_->codingSystem;
1346               sos.codingSystemName = defSpec_->codingSystemName;
1347             }
1348             else {
1349               sos.codingSystem = em_->defaultCodingSystem_;
1350               sos.codingSystemName = 0;
1351             }
1352           }
1353         }
1354         else
1355           mgr_.message(EntityManagerMessages::fsiUnknownBctf,
1356                        StringMessageArg(value));
1357       }
1358       else
1359         mgr_.message(EntityManagerMessages::fsiMissingValue,
1360                      StringMessageArg(token));
1361       hadBctf = 1;
1362     }
1363     else if (matchKey(token, "TRACKING")) {
1364       if (hadTracking)
1365         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1366                      StringMessageArg(token));
1367       else if (gotValue) {
1368         if (matchKey(value, "NOTRACK"))
1369           sos.notrack = 1;
1370         else if (!matchKey(value, "TRACK"))
1371           mgr_.message(EntityManagerMessages::fsiBadTracking,
1372                        StringMessageArg(value));
1373       }
1374       else
1375         mgr_.message(EntityManagerMessages::fsiMissingValue,
1376                      StringMessageArg(token));
1377       hadTracking = 1;
1378     }
1379     else if (matchKey(token, "ZAPEOF")) {
1380       if (sos.storageManager->requiredCodingSystem())
1381         mgr_.message(EntityManagerMessages::fsiZapeofNotApplicable);
1382       else if (hadZapeof)
1383         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1384                      StringMessageArg(token));
1385       else if (gotValue) {
1386         if (matchKey(value, "ZAPEOF"))
1387           sos.zapEof = 1;
1388         else if (matchKey(value, "NOZAPEOF"))
1389           sos.zapEof = 0;
1390         else
1391           mgr_.message(EntityManagerMessages::fsiBadZapeof,
1392                        StringMessageArg(value));
1393       }
1394       else
1395         sos.zapEof = 1;
1396       hadZapeof = 1;
1397     }
1398     else if (matchKey(token, "NOZAPEOF")) {
1399       if (sos.storageManager->requiredCodingSystem())
1400         mgr_.message(EntityManagerMessages::fsiZapeofNotApplicable);
1401       else if (hadZapeof)
1402         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1403                      StringMessageArg(idCharset_.execToDesc("ZAPEOF")));
1404       else if (gotValue)
1405         mgr_.message(EntityManagerMessages::fsiValueAsName,
1406                      StringMessageArg(token));
1407       else
1408         sos.zapEof = 0;
1409       hadZapeof = 1;
1410     }
1411     else if (matchKey(token, "SEARCH")) {
1412       if (hadSearch)
1413         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1414                      StringMessageArg(token));
1415       else if (gotValue) {
1416         if (matchKey(value, "SEARCH"))
1417           sos.search = 1;
1418         else if (matchKey(value, "NOSEARCH"))
1419           sos.search = 0;
1420         else
1421           mgr_.message(EntityManagerMessages::fsiBadSearch,
1422                        StringMessageArg(value));
1423       }
1424       else
1425         sos.search = 1;
1426       hadSearch = 1;
1427     }
1428     else if (matchKey(token, "NOSEARCH")) {
1429       if (hadSearch)
1430         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1431                      StringMessageArg(idCharset_.execToDesc("SEARCH")));
1432       else if (gotValue)
1433         mgr_.message(EntityManagerMessages::fsiValueAsName,
1434                      StringMessageArg(token));
1435       else
1436         sos.search = 0;
1437       hadSearch = 1;
1438     }
1439     else if (matchKey(token, "FOLD")) {
1440       if (!neutral)
1441         mgr_.message(EntityManagerMessages::fsiFoldNotNeutral);
1442       else if (hadFold)
1443         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1444                      StringMessageArg(token));
1445       else if (gotValue) {
1446         if (matchKey(value, "FOLD"))
1447           fold = 1;
1448         else if (matchKey(value, "NOFOLD"))
1449           fold = 0;
1450         else
1451           mgr_.message(EntityManagerMessages::fsiBadFold,
1452                        StringMessageArg(value));
1453       }
1454       else
1455         fold = 1;
1456       hadFold = 1;
1457     }
1458     else if (matchKey(token, "NOFOLD")) {
1459       if (!neutral)
1460         mgr_.message(EntityManagerMessages::fsiFoldNotNeutral);
1461       else if (hadFold)
1462         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1463                      StringMessageArg(idCharset_.execToDesc("FOLD")));
1464       else if (gotValue)
1465         mgr_.message(EntityManagerMessages::fsiValueAsName,
1466                      StringMessageArg(token));
1467       else
1468         fold = 0;
1469       hadFold = 1;
1470     }
1471     else if (matchKey(token, "SMCRD")) {
1472       if (hadSmcrd)
1473         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1474                      StringMessageArg(token));
1475       else if (gotValue) {
1476         if (value.size() == 0)
1477           smcrd = -1;
1478         else if (value.size() == 1)
1479           smcrd = value[0];
1480         else
1481           mgr_.message(EntityManagerMessages::fsiBadSmcrd,
1482                        StringMessageArg(value));
1483       }
1484       else
1485         mgr_.message(EntityManagerMessages::fsiMissingValue,
1486                      StringMessageArg(token));
1487       hadSmcrd = 1;
1488     }
1489     else if (matchKey(token, "RECORDS")) {
1490       if (sos.storageManager->requiresCr())
1491         mgr_.message(EntityManagerMessages::fsiRecordsNotApplicable);
1492       else if (hadRecords)
1493         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1494                      StringMessageArg(token));
1495       else if (gotValue) {
1496         if (!lookupRecords(value, sos.records))
1497           mgr_.message(EntityManagerMessages::fsiUnsupportedRecords,
1498                        StringMessageArg(value));
1499       }
1500       else
1501         mgr_.message(EntityManagerMessages::fsiMissingValue,
1502                      StringMessageArg(token));
1503       hadRecords = 1;
1504     }
1505     else if (matchKey(token, "SOIBASE")) {
1506       if (hadBase)
1507         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1508                      StringMessageArg(token));
1509       else if (gotValue)
1510         value.swap(sos.baseId);
1511       else {
1512         mgr_.message(EntityManagerMessages::fsiMissingValue,
1513                      StringMessageArg(token));
1514         sos.baseId.resize(0);
1515       }
1516       hadBase = 1;
1517     }
1518     else if (lookupRecords(token, records)) {
1519       if (sos.storageManager->requiresCr())
1520         mgr_.message(EntityManagerMessages::fsiRecordsNotApplicable);
1521       else if (hadRecords)
1522         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1523                      StringMessageArg(idCharset_.execToDesc("RECORDS")));
1524       else if (!gotValue)
1525         sos.records = records;
1526       else
1527         mgr_.message(EntityManagerMessages::fsiValueAsName,
1528                      StringMessageArg(token));
1529       hadRecords = 1;
1530     }
1531     else if (matchKey(token, "NOTRACK")) {
1532       if (hadTracking)
1533         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1534                      StringMessageArg(idCharset_.execToDesc("TRACKING")));
1535       else if (!gotValue)
1536         sos.notrack = 1;
1537       else
1538         mgr_.message(EntityManagerMessages::fsiValueAsName,
1539                      StringMessageArg(token));
1540       hadTracking = 1;
1541     }
1542     else if (matchKey(token, "TRACK")) {
1543       if (hadTracking)
1544         mgr_.message(EntityManagerMessages::fsiDuplicateAttribute,
1545                      StringMessageArg(idCharset_.execToDesc("TRACKING")));
1546       else if (gotValue)
1547         mgr_.message(EntityManagerMessages::fsiValueAsName,
1548                      StringMessageArg(token));
1549       hadTracking = 1;
1550     }
1551     else
1552       mgr_.message(gotValue
1553                    ? EntityManagerMessages::fsiUnsupportedAttribute
1554                    : EntityManagerMessages::fsiUnsupportedAttributeToken,
1555                    StringMessageArg(token));
1556   }
1557   if (hadBase && sos.baseId.size() > 0) {
1558     convertId(sos.baseId, smcrd, sos.storageManager);
1559     if (neutral) {
1560       if (!sos.storageManager->transformNeutral(sos.baseId, fold, mgr_))
1561         sos.baseId.resize(0);
1562     }
1563   }
1564   if (!hadZapeof && hadRecords && sos.records == StorageObjectSpec::asis)
1565     sos.zapEof = 0;
1566   return 1;
1567 }
1568
1569 FSIParser::RecordType FSIParser::recordTypeTable[] = {
1570   { "FIND", StorageObjectSpec::find },
1571   { "ASIS", StorageObjectSpec::asis },
1572   { "CR", StorageObjectSpec::cr },
1573   { "LF", StorageObjectSpec::lf },
1574   { "CRLF", StorageObjectSpec::crlf }
1575 };
1576
1577 const char *FSIParser::recordsName(StorageObjectSpec::Records records)
1578 {
1579   for (size_t i = 0; i < SIZEOF(recordTypeTable); i++)
1580     if (records == recordTypeTable[i].value)
1581       return recordTypeTable[i].name;
1582   return 0;
1583 }
1584
1585 Boolean FSIParser::lookupRecords(const StringC &token,
1586                                  StorageObjectSpec::Records &result)
1587 {
1588   for (size_t i = 0; i < SIZEOF(recordTypeTable); i++)
1589     if (matchKey(token, recordTypeTable[i].name)) {
1590       result = recordTypeTable[i].value;
1591       return 1;
1592     }
1593   return 0;
1594 }
1595
1596 void FSIParser::setDefaults(StorageObjectSpec &sos)
1597 {
1598   if (sos.storageManager->requiresCr())
1599     sos.records = StorageObjectSpec::cr;
1600   else if (isNdata_
1601            || (defSpec_ && defSpec_->records == StorageObjectSpec::asis))
1602     sos.records = StorageObjectSpec::asis;
1603   if (isNdata_ || (defSpec_ && !defSpec_->zapEof))
1604     sos.zapEof = 0;
1605   if (defSpec_ && defSpec_->storageManager == sos.storageManager) {
1606     if (defSpec_->id.size())
1607       sos.baseId = defSpec_->id;
1608     else {
1609       sos.baseId = defSpec_->specId;
1610       sos.storageManager->resolveRelative(defSpec_->baseId,
1611                                           sos.baseId,
1612                                           0);
1613     }
1614   }
1615   sos.codingSystem = sos.storageManager->requiredCodingSystem();
1616   if (sos.codingSystem)
1617     sos.zapEof = 0;             // hack
1618   else {
1619     sos.codingSystem = em_->defaultCodingSystem_;
1620     if (isNdata_) {
1621       const InputCodingSystem *id = 0;
1622       size_t j;
1623       for (j = 0; j < em_->codingSystems_.size(); j++)
1624         if (em_->codingSystems_[j].ics->isIdentity()) {
1625           id = em_->codingSystems_[j].ics;
1626           break;
1627         }
1628       if (id && id != em_->defaultCodingSystem_) {
1629         sos.codingSystem = id;
1630         sos.codingSystemName = em_->codingSystems_[j].name;
1631       }
1632     }
1633     else if (defSpec_) {
1634       sos.codingSystem = defSpec_->codingSystem;
1635       sos.codingSystemName = defSpec_->codingSystemName;
1636     }
1637   }
1638 }
1639
1640 Boolean FSIParser::parseAttribute(StringC &token, Boolean &gotValue,
1641                                   StringC &value)
1642 {
1643   Xchar c = get();
1644   while (isS(c))
1645     c = get();
1646   if (c == -1) {
1647     return 0;
1648   }
1649   token.resize(0);
1650   if (matchChar(c, '>'))
1651     return 1;
1652   if (matchChar(c, '"') || matchChar(c, '\'') || matchChar(c, '='))
1653     return 0;
1654   for (;;) {
1655     token += c;
1656     c = get();
1657     if (c == -1)
1658       return 0;
1659     if (isS(c))
1660       break;
1661     if (matchChar(c, '>') || matchChar(c, '='))
1662       break;
1663   }
1664   while (isS(c))
1665     c = get();
1666   if (c == -1)
1667     return 0;
1668   if (!matchChar(c, '=')) {
1669     unget();
1670     gotValue = 0;
1671     return 1;
1672   }
1673   gotValue = 1;
1674   value.resize(0);
1675
1676   c = get();
1677   while (isS(c))
1678     c = get();
1679   if (matchChar(c, '>') || matchChar(c, '='))
1680     return 0;
1681   if (matchChar(c, '"') || matchChar(c, '\'')) {
1682     Char lit = c;
1683     for (;;) {
1684       Xchar c = get();
1685       if (c == lit)
1686         break;
1687       if (c == -1)
1688         return 0;
1689       if (matchChar(c, '\n'))
1690         ;
1691       else if (matchChar(c, '\r') || matchChar(c, '\t'))
1692         value += idCharset_.execToDesc(' ');
1693       else
1694         value += c;
1695     }
1696     uncharref(value);
1697   }
1698   else {
1699     for (;;) {
1700       value += c;
1701       c = get();
1702       if (c == -1)
1703         return 0;
1704       if (isS(c))
1705         break;
1706       if (matchChar(c, '>') || matchChar(c, '=')) {
1707         unget();
1708         break;
1709       }
1710     }
1711   }
1712   return 1;
1713 }
1714
1715 void FSIParser::uncharref(StringC &str)
1716 {
1717   size_t j = 0;
1718   size_t i = 0;
1719   while (i < str.size()) {
1720     int digit;
1721     if (matchChar(str[i], '&')
1722         && i + 2 < str.size()
1723         && matchChar(str[i + 1], '#')
1724         && convertDigit(str[i + 2], digit)) {
1725       unsigned long val = digit;
1726       i += 3;
1727       while (i < str.size() && convertDigit(str[i], digit)) {
1728         val = val*10 + digit;
1729         i++;
1730       }
1731       str[j++] = val;
1732       if (i < str.size() && matchChar(str[i], ';'))
1733         i++;
1734     }
1735     else
1736       str[j++] = str[i++];
1737   }
1738   str.resize(j);
1739 }
1740
1741 Boolean FSIParser::convertId(StringC &id, Xchar smcrd,
1742                              const StorageManager *sm)
1743 {
1744   const CharsetInfo *smCharset = sm->idCharset();
1745   StringC newId;
1746   size_t i = 0;
1747   while (i < id.size()) {
1748     UnivChar univ;
1749     WideChar wide;
1750     ISet<WideChar> wideSet;
1751     int digit;
1752     if (Xchar(id[i]) == smcrd
1753         && i + 1 < id.size()
1754         && convertDigit(id[i + 1], digit)) {
1755       i += 2;
1756       Char val = digit;
1757       while (i < id.size() && convertDigit(id[i], digit)) {
1758         val = val*10 + digit;
1759         i++;
1760       }
1761       newId += val;
1762       if (i < id.size() && matchChar(id[i], ';'))
1763         i++;
1764     }
1765     else if (smCharset) {
1766       if (!idCharset_.descToUniv(id[i++], univ))
1767         return 0;
1768       if (univ == UnivCharsetDesc::rs)
1769         ;
1770       else if (univ == UnivCharsetDesc::re && sm->reString())
1771         newId += *sm->reString();
1772       else if (smCharset->univToDesc(univ, wide, wideSet) != 1
1773                || wide > charMax)
1774         return 0;                       // FIXME give error
1775       else
1776         newId += Char(wide);
1777     }
1778     else
1779       newId += id[i++];
1780   }
1781   newId.swap(id);
1782   return 1;
1783 }
1784
1785 ParsedSystemId:: ParsedSystemId()
1786 {
1787 }
1788
1789 static
1790 void unparseSoi(const StringC &soi,
1791                 const CharsetInfo *idCharset,
1792                 const CharsetInfo &resultCharset,
1793                 StringC &result,
1794                 Boolean &needSmcrd);
1795
1796 void ParsedSystemId::unparse(const CharsetInfo &resultCharset,
1797                              StringC &result) const
1798 {
1799   size_t len = size();
1800   result.resize(0);
1801   size_t i;
1802   for (i = 0; i < maps.size(); i++) {
1803     if (maps[i].type == ParsedSystemIdMap::catalogDocument)
1804       result += resultCharset.execToDesc("<CATALOG>");
1805     else if (maps[i].type == ParsedSystemIdMap::catalogPublic) {
1806       result += resultCharset.execToDesc("<CATALOG PUBLIC=\"");
1807       result += maps[i].publicId;
1808       result += resultCharset.execToDesc("\">");
1809     }
1810   }
1811   for (i = 0; i < len; i++) {
1812     const StorageObjectSpec &sos = (*this)[i];
1813     result += resultCharset.execToDesc('<');
1814     result += resultCharset.execToDesc(sos.storageManager->type());
1815     if (sos.notrack)
1816       result += resultCharset.execToDesc(" NOTRACK");
1817     if (!sos.search)
1818       result += resultCharset.execToDesc(" NOSEARCH");
1819     if (!sos.storageManager->requiresCr()
1820         && sos.records != StorageObjectSpec::find) {
1821       result += resultCharset.execToDesc(' ');
1822       result += resultCharset.execToDesc(FSIParser::recordsName(sos.records));
1823     }
1824     if (sos.codingSystemName) {
1825       if (!sos.zapEof)
1826         result += resultCharset.execToDesc(" NOZAPEOF");
1827       result += resultCharset.execToDesc(" BCTF=");
1828       result += resultCharset.execToDesc(sos.codingSystemName);
1829     }
1830     Boolean needSmcrd = 0;
1831     if (sos.baseId.size() != 0) {
1832       result += resultCharset.execToDesc(" SOIBASE='");
1833       unparseSoi(sos.baseId,
1834                  sos.storageManager->idCharset(),
1835                  resultCharset,
1836                  result,
1837                  needSmcrd);
1838       result += resultCharset.execToDesc('\'');
1839     }
1840     StringC tem;
1841     unparseSoi(sos.specId,
1842                sos.storageManager->idCharset(),
1843                resultCharset,
1844                tem,
1845                needSmcrd);
1846     if (needSmcrd)
1847       result += resultCharset.execToDesc(" SMCRD='^'");
1848     result += resultCharset.execToDesc('>');
1849     result += tem;
1850   }
1851 }
1852
1853 void unparseSoi(const StringC &soi,
1854                 const CharsetInfo *idCharset,
1855                 const CharsetInfo &resultCharset,
1856                 StringC &result,
1857                 Boolean &needSmcrd)
1858 {
1859   if (!idCharset) {
1860     for (size_t i = 0; i < soi.size(); i++) {
1861       char buf[32];
1862       sprintf(buf, "&#%lu;", (unsigned long)soi[i]);
1863       result += resultCharset.execToDesc(buf);
1864     }
1865     return;
1866   }
1867   for (size_t i = 0; i < soi.size(); i++) {
1868     UnivChar univ;
1869     WideChar to;
1870     ISet<WideChar> toSet;
1871     if (!idCharset->descToUniv(soi[i], univ)
1872         || univ >= 127
1873         || univ < 32
1874         || univ == 36           // $
1875         || univ == 96           // `
1876 #ifndef MSDOS_FILENAMES
1877         || univ == 92           // backslash
1878 #endif
1879         || univ == 94           // ^
1880         || resultCharset.univToDesc(univ, to, toSet) != 1) {
1881       needSmcrd = 1;
1882       char buf[32];
1883       sprintf(buf, "^%lu;", (unsigned long)soi[i]);
1884       result += resultCharset.execToDesc(buf);
1885     }
1886     else {
1887       switch (univ) {
1888       case 34:          // double quote
1889       case 35:          // #
1890       case 39:          // apostrophe
1891       case 60:          // <
1892         {
1893           char buf[32];
1894           sprintf(buf, "&#%lu;", (unsigned long)to);
1895           result += resultCharset.execToDesc(buf);
1896         }
1897         break;
1898       default:
1899         result += Char(to);
1900         break;
1901       }
1902     }
1903   }
1904 }
1905
1906 #ifdef SP_NAMESPACE
1907 }
1908 #endif