libDtSearch: Remove optional code for NO_DBN which is not used on CDE
[oweals/cde.git] / cde / programs / nsgmls / RastEventHandler.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: RastEventHandler.C /main/1 1996/07/29 17:02:26 cde-hp $ */
24 // Copyright (c) 1994,1995 James Clark
25 // See the file COPYING for copying permission.
26
27 #ifdef __GNUG__
28 #pragma implementation
29 #endif
30
31 #include "config.h"
32 #include "RastEventHandler.h"
33 #include "SgmlParser.h"
34 #include "ParserOptions.h"
35 #include "Entity.h"
36 #include "Notation.h"
37 #include "Attribute.h"
38 #include "Vector.h"
39 #include "Vector.h"
40 #include "MessageArg.h"
41
42 #include "RastEventHandlerMessages.h"
43
44 #include <stdlib.h>
45 #include <string.h>
46
47 // This is based on ISO/IEC 13673, Intermediate Editor's Draft, 1994/8/29,
48 // together with editing instructions in ISO/IEC JTC1/SC18/WG8 N1777.
49
50 #ifdef SP_NAMESPACE
51 namespace SP_NAMESPACE {
52 #endif
53
54 const OutputCharStream::Newline nl = OutputCharStream::newline;
55
56 class EventHandlerMessenger : public Messenger {
57 public:
58   EventHandlerMessenger(EventHandler *eh) : eh_(eh) { }
59   void dispatchMessage(const Message &message) {
60     eh_->message(new MessageEvent(message));
61   }
62   void dispatchMessage(Message &message) {
63     eh_->message(new MessageEvent(message));
64   }
65 private:
66   EventHandler *eh_;
67 };
68
69 #if 0
70 const 
71 #endif
72 RastPrintable RastEventHandler::printable;
73
74 RastPrintable::RastPrintable()
75 {
76   static const char s[] =
77     " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
78   size_t i;
79   for (i = 0; i < sizeof(v_); i++)
80     v_[i] = 0;
81   for (i = 0; s[i] != '\0'; i++)
82     v_[(unsigned char)s[i]] = 32 + i;
83 }
84
85 // inline
86 void RastEventHandler::flushLine(LineType type)
87 {
88   if (lineLength_ > 0) {
89     os() << char(type) << nl;
90     lineLength_ = 0;
91   }
92 }
93
94 RastSubdocState::RastSubdocState()
95 {
96   init(0, 0);
97 }
98
99 RastSubdocState::RastSubdocState(SgmlParser *parser, RastEventHandler *rast)
100 {
101   init(parser, rast);
102 }
103
104 void RastSubdocState::init(SgmlParser *parser, RastEventHandler *rast)
105 {
106   parser_ = parser;
107   hadActiveLpdOrDtd_ = 0;
108   activeLinkTypes_.clear();
109   hadDocumentElement_ = 0;
110   linkProcess_.clear();
111   linkProcess_.setHandler(rast);
112   haveLinkProcess_ = 0;
113   endPrologEvent_.clear();
114   parseSubdocQueue_.clear();
115   linkRuleQueue_.clear();
116   for (int i = 0; i < nAttributeType; i++)
117     attributeSortOrder_[i].clear();
118 }
119
120 void RastSubdocState::swap(RastSubdocState &to)
121 {
122   {
123     SgmlParser *tem = to.parser_;
124     to.parser_ = parser_;
125     parser_ = tem;
126   }
127   {
128     Boolean tem = to.hadActiveLpdOrDtd_;
129     to.hadActiveLpdOrDtd_ = hadActiveLpdOrDtd_;
130     hadActiveLpdOrDtd_ = tem;
131   }
132   {
133     Boolean tem = to.hadDocumentElement_;
134     to.hadDocumentElement_ = hadDocumentElement_;
135     hadDocumentElement_ = tem;
136   }
137   activeLpdOrDtdLocation_.swap(to.activeLpdOrDtdLocation_);
138   activeLinkTypes_.swap(to.activeLinkTypes_);
139   linkProcess_.swap(to.linkProcess_);
140   endPrologEvent_.swap(to.endPrologEvent_);
141   parseSubdocQueue_.swap(to.parseSubdocQueue_);
142   linkRuleQueue_.swap(to.linkRuleQueue_);
143   for (int i = 0; i < nAttributeType; i++)
144     attributeSortOrder_[i].swap(to.attributeSortOrder_[i]);
145 }
146
147 RastEventHandler::RastEventHandler(SgmlParser *parser, Messenger *mgr)
148 : lineLength_(0),
149   os_(0),
150   piErrorCount_(0),
151   RastSubdocState(parser, this),
152   mgr_(mgr)
153 {
154   RastSubdocState::init(parser, this);
155 }
156
157 void RastEventHandler::end()
158 {
159   if (errorCount() != 0) {
160     truncateOutput();
161     os() << (piErrorCount_ != 0
162              ? "#RAST-PI-ERROR"
163              : "#ERROR")
164          << nl;
165   }
166 }
167
168 void RastEventHandler::truncateOutput()
169 {
170   // This must be handled by derived classes to get conforming output.
171 }
172
173 void RastEventHandler::sgmlDecl(SgmlDeclEvent *event)
174 {
175   rastParseSubdocYesString_ = event->sd().execToDoc("rast-parse-subdoc:yes");
176   rastParseSubdocNoString_ = event->sd().execToDoc("rast-parse-subdoc:no");
177   rastActiveLpdString_ = event->sd().execToDoc("rast-active-lpd:");
178   rastLinkRuleString_ = event->sd().execToDoc("rast-link-rule:");
179   delete event;
180 }
181
182 void RastEventHandler::startElement(StartElementEvent *event)
183 {
184   flushLine(dataLine);
185   if (!hadDocumentElement_) {
186     if (activeLinkTypes_.size() > 0) {
187       activeLinks();
188       simpleLinkInfo();
189     }
190     hadDocumentElement_ = 1;
191   }
192   os() << '[' << event->name();
193   Boolean hadNewline;
194   if (event->attributes().size() > 0) {
195     hadNewline = 1;
196     os() << nl;
197     attributeInfo(event->attributes(), dtdAttribute);
198   }
199   else
200     hadNewline = 0;
201   if (haveLinkProcess_) {
202     const AttributeList *linkAttributes;
203     const ResultElementSpec *resultElementSpec;
204     EventHandlerMessenger messenger(this);
205     linkProcess_.startElement(event->elementType(),
206                               event->attributes(),
207                               event->location(),
208                               messenger,
209                               linkAttributes,
210                               resultElementSpec);
211     if (linkProcess_.nImpliedLinkRules() > 0) {
212       if (!hadNewline) {
213         os() << nl;
214         hadNewline = 1;
215       }
216       os() << "#LINK-SET-INFO" << nl;
217       impliedSourceLinkRules();
218     }
219     if (linkAttributes) {
220       if (!hadNewline) {
221         os() << nl;
222         hadNewline = 1;
223       }
224       os() << "#LINK-RULE" << nl;
225       attributeInfo(*linkAttributes, linkAttribute);
226       if (linkProcess_.isExplicit()) {
227         os() << "#RESULT=";
228         if (resultElementSpec && resultElementSpec->elementType) {
229           os() << resultElementSpec->elementType->name() << nl;
230           attributeInfo(resultElementSpec->attributeList, resultAttribute);
231         }
232         else
233           os() << "#IMPLIED" << nl;
234       }
235     }
236     else
237       hadNewline = 0;
238   }
239   os() << ']' << nl;
240   delete event;
241 }
242
243 void RastEventHandler::activeLinks()
244 {
245   for (size_t i = 0; i < activeLinkTypes_.size(); i++) {
246     os() << "#ACTIVE-LINK=" << activeLinkTypes_[i] << nl;
247     Boolean found = 0;
248     if (haveLinkProcess_ && linkProcess_.name() == activeLinkTypes_[i]) {
249       found = 1;
250       if (linkProcess_.nImpliedLinkRules() > 0) {
251         os() << "#INITIAL" << nl;
252         impliedSourceLinkRules();
253       }
254     }
255     if (!found) {
256       if (endPrologEvent_) {
257         for (size_t j = 0; j < endPrologEvent_->simpleLinkNames().size(); j++)
258           if (endPrologEvent_->simpleLinkNames()[j] == activeLinkTypes_[i]) {
259             found = 1;
260             break;
261           }
262       }
263       if (!found) {
264         setNextLocation(activeLpdOrDtdLocation_);
265         Messenger::message(RastEventHandlerMessages::invalidActiveLinkType,
266                            StringMessageArg(activeLinkTypes_[i]));
267       }
268     }
269     os() << "#END-ACTIVE-LINK" << nl;
270   }
271 }
272
273 void RastEventHandler::simpleLinkInfo()
274 {
275   if (!endPrologEvent_)
276     return;
277   for (size_t i = 0; i < activeLinkTypes_.size(); i++) {
278     for (size_t j = 0; j < endPrologEvent_->simpleLinkNames().size(); j++) {
279       const StringC &name = endPrologEvent_->simpleLinkNames()[j];
280       if (name == activeLinkTypes_[i]) {
281         os() << "#SIMPLE-LINK=" << name << nl;
282         if (endPrologEvent_->simpleLinkAttributes()[j].size() > 0)
283           attributeInfo(endPrologEvent_->simpleLinkAttributes()[j],
284                         simpleAttribute);
285         os() << "#END-SIMPLE-LINK" << nl;
286         break;
287       }
288     }
289   }
290 }
291
292 void RastEventHandler::impliedSourceLinkRules()
293 {
294   size_t n = linkProcess_.nImpliedLinkRules();
295   Vector<size_t> sortOrder(n);
296   size_t i;
297   for (i = 0; i < n; i++)
298     sortOrder[i] = i;
299   for (i = 1; i < n; i++) {
300     size_t tem = sortOrder[i];
301     const StringC &name
302       = linkProcess_.impliedLinkRule(tem).elementType->name();
303     size_t j;
304     for (j = i; j > 0; j--) {
305       if (lexCmp(linkProcess_.impliedLinkRule(j - 1).elementType->name(),
306                  name) <= 0)
307         break;
308       sortOrder[j] = sortOrder[j - 1];
309     }
310     sortOrder[j] = tem;
311   }
312   for (i = 0; i < n; i++) {
313     const ResultElementSpec &result
314       = linkProcess_.impliedLinkRule(sortOrder[i]);
315     os() << '[' << result.elementType->name();
316     if (result.attributeList.size() > 0) {
317       os() << nl;
318       attributeInfo(result.attributeList, resultAttribute);
319     }
320     os() << ']' << nl;
321   }
322 }
323
324 void RastEventHandler::endElement(EndElementEvent *event)
325 {
326   if (haveLinkProcess_)
327     linkProcess_.endElement();
328   flushLine(dataLine);
329   os() << "[/" << event->name() << ']' << nl;
330   if (haveLinkProcess_ && linkProcess_.nImpliedLinkRules() > 0) {
331     os() << "#LINK-SET-INFO" << nl;
332     impliedSourceLinkRules();
333     os() << "#END-LINK-SET-INFO" << nl;
334   }
335   delete event;
336 }
337
338 void RastEventHandler::data(DataEvent *event)
339 {
340   lines(dataLine, event->data(), event->dataLength());
341   delete event;
342 }
343
344 void RastEventHandler::pi(PiEvent *event)
345 {
346   flushLine(dataLine);
347   os() << "[?";
348   size_t dataLength = event->dataLength();
349   if (dataLength > 0) {
350     const Char *data = event->data();
351     if (dataLength >= 4
352         && memcmp(data,
353                   rastParseSubdocYesString_.data(),
354                   4*sizeof(Char)) == 0
355         && !interpretRastPi(data, dataLength, event->location())) {
356       setNextLocation(event->location());
357       Messenger::message(RastEventHandlerMessages::invalidRastPiError);
358     }
359     os() << nl;
360     lines(dataLine, event->data(), dataLength);
361     flushLine(dataLine);
362   }
363   os() << ']' << nl;
364   delete event;
365 }
366
367 inline
368 Boolean equal(const Char *s1, size_t n1, const StringC &s2)
369 {
370   return (n1 == s2.size()
371           && (n1 == 0
372               || memcmp(s1, s2.data(), n1*sizeof(Char)) == 0));
373 }
374
375 // Is s2 a prefix of s1 of length n1?
376
377 inline
378 Boolean prefix(const Char *s1, size_t n1, const StringC &s2)
379 {
380   return (n1 >= s2.size()
381           && (n1 == 0
382               || memcmp(s1, s2.data(), s2.size()*sizeof(Char)) == 0));
383 }
384
385 Boolean RastEventHandler::interpretRastPi(const Char *data,
386                                           size_t dataLength,
387                                           const Location &loc)
388 {
389   if (equal(data, dataLength, rastParseSubdocNoString_)) {
390     queueParseSubdoc(0);
391     return 1;
392   }
393   if (equal(data, dataLength, rastParseSubdocYesString_)) {
394     queueParseSubdoc(1);
395     return 1;
396   }
397   if (prefix(data, dataLength, rastActiveLpdString_)) {
398     if (hadActiveLpdOrDtd_)
399       return 1;
400     hadActiveLpdOrDtd_ = 1;
401     activeLpdOrDtdLocation_ = loc;
402     const Char *p = data + rastActiveLpdString_.size();
403     size_t n = dataLength - rastActiveLpdString_.size();
404     StringC name;
405     for (;;) {
406       if (n == 0 || *p == ',') {
407         if (name.size() == 0)
408           return 0;
409         for (size_t i = 0; i < activeLinkTypes_.size(); i++)
410           if (name == activeLinkTypes_[i]) {
411             setNextLocation(activeLpdOrDtdLocation_);
412             Messenger::message(RastEventHandlerMessages::duplicateActiveLinkType,
413                                StringMessageArg(name));
414           }
415         activeLinkTypes_.resize(activeLinkTypes_.size() + 1);
416         name.swap(activeLinkTypes_.back());
417         if (n == 0)
418           break;
419       }
420       else
421         name += *p;
422       p++;
423       n--;
424     }
425     for (size_t i = 0; i < activeLinkTypes_.size(); i++)
426       parser_->activateLinkType(activeLinkTypes_[i]);
427     return 1;
428   }
429   if (prefix(data, dataLength, rastLinkRuleString_)) {
430     LinkRulePi *p = new LinkRulePi;
431     p->pi.assign(data + rastLinkRuleString_.size(),
432                  dataLength - rastLinkRuleString_.size());
433     p->loc = loc;
434     linkRuleQueue_.append(p);
435     return 1;
436   }
437   return 0;
438 }
439
440 void RastEventHandler::sdataEntity(SdataEntityEvent *event)
441 {
442   flushLine(dataLine);
443   os() << "#SDATA-TEXT" << nl;
444   lines(markupLine, event->data(), event->dataLength());
445   flushLine(markupLine);
446   os() << "#END-SDATA" << nl;
447   delete event;
448 }
449
450 void RastEventHandler::externalDataEntity(ExternalDataEntityEvent *event)
451 {
452   const ExternalDataEntity *entity = event->entity();
453   if (!entity)
454     return;
455   flushLine(dataLine);
456   os() << "[&" << entity->name() << nl;
457   externalEntityInfo(entity, dtdAttribute);
458   os() << ']' << nl;
459   delete event;
460 }
461
462 void RastEventHandler::externalEntityInfo(const ExternalDataEntity *entity,
463                                           AttributeType attributeType)
464 {
465   char c;
466   switch (entity->dataType()) {
467   case Entity::cdata:
468     c = 'C';
469     break;
470   case Entity::sdata:
471     c = 'S';
472     break;
473   case Entity::ndata:
474     c = 'N';
475     break;
476   default:
477     return;
478   }
479   os() << '#' << c << "DATA-EXTERNAL" << nl;
480   externalIdInfo(entity->externalId());
481   os() << "#NOTATION=" << entity->notation()->name() << nl;
482   externalIdInfo(entity->notation()->externalId());
483   attributeInfo(entity->attributes(),
484                 (attributeType == resultAttribute
485                  ? resultAttribute
486                  : dtdAttribute));
487 }
488
489 void RastEventHandler::subdocEntity(SubdocEntityEvent *event)
490 {
491   const SubdocEntity *entity = event->entity();
492   if (!entity)
493     return;
494   flushLine(dataLine);
495   os() << "[&" << entity->name() << nl;
496   Ptr<InputSourceOrigin> origin(event->entityOrigin()->copy());
497   subdocEntityInfo(entity, origin, 1);
498   os() << ']' << nl;
499   delete event;
500 }
501
502 void RastEventHandler::subdocEntityInfo(const SubdocEntity *entity,
503                                         const Ptr<InputSourceOrigin> &entityOrigin,
504                                         Boolean referenced)
505 {
506   os() << "#SUBDOC" << nl;
507   externalIdInfo(entity->externalId());
508   if (parseNextSubdoc()) {
509     // FIXME subdocuments in entity attributes shouldn't count against
510     // SUBDOC quantity limit.
511     os() << "#PARSED-SUBDOCUMENT" << nl;
512     SgmlParser::Params params;
513     params.entityType = SgmlParser::Params::subdoc;
514     params.subdocInheritActiveLinkTypes = 0;
515     params.subdocReferenced = referenced;
516     params.parent = parser_;
517     params.sysid = entity->externalId().effectiveSystemId();
518     params.origin = entityOrigin;
519     SgmlParser parser(params);
520     RastSubdocState oldSubdocState;
521     RastSubdocState::swap(oldSubdocState);
522     RastSubdocState::init(&parser, this);
523     parser.parseAll(*this);
524     oldSubdocState.swap(*this);
525   }
526 }
527
528 void RastEventHandler::queueParseSubdoc(Boolean parseSubdoc)
529 {
530   parseSubdocQueue_.push_back(PackedBoolean(parseSubdoc));
531 }
532
533 Boolean RastEventHandler::parseNextSubdoc()
534 {
535   if (parseSubdocQueue_.size() == 0)
536     return 0;
537   Boolean result = parseSubdocQueue_[0];
538   if (parseSubdocQueue_.size() > 1) {
539     for (size_t i = 1; i < parseSubdocQueue_.size(); i++)
540       parseSubdocQueue_[i - 1] = parseSubdocQueue_[i];
541   }
542   parseSubdocQueue_.resize(parseSubdocQueue_.size() - 1);
543   return result;
544 }
545
546
547 void RastEventHandler::externalIdInfo(const ExternalId &id)
548 {
549   const StringC *systemId = id.systemIdString();
550   const StringC *publicId = id.publicIdString();
551   if (publicId) {
552     os() << "#PUBLIC" << nl;
553     if (publicId->size() == 0)
554       os() << "#EMPTY" << nl;
555     else {
556       lines(markupLine, publicId->data(), publicId->size());
557       flushLine(markupLine);
558     }
559   }
560   if (systemId || !publicId) {
561     os() << "#SYSTEM" << nl;
562     if (!systemId)
563       os() << "#NONE" << nl;
564     else if (systemId->size() == 0)
565       os() << "#EMPTY" << nl;
566     else {
567       lines(markupLine, systemId->data(), systemId->size());
568       flushLine(markupLine);
569     }
570   }
571 }
572
573 void RastEventHandler::lines(LineType type, const Char *p, size_t length)
574 {
575   // This needs to be fast.
576   while (length != 0) {
577     if (printable(*p)) {
578       size_t lim;
579       switch (lineLength_) {
580       case maxLineLength:
581         os() << char(type) << nl;
582         lineLength_ = 0;
583         // fall through
584       case 0:
585         os() << char(type);
586         lim = maxLineLength;
587         break;
588       default:
589         lim = maxLineLength - lineLength_;
590         break;
591       }
592       if (lim > length)
593         lim = length;
594       size_t n = lim;
595       for (;;) {
596         os().put(*p);
597         p++;
598         if (--n == 0)
599           break;
600         if (!printable(*p)) {
601           lim -= n;
602           break;
603         }
604       }
605       length -= lim;
606       lineLength_ += lim;
607     }
608     else {
609       // *p is an unprintable character print it
610       flushLine(type);
611       switch (*p) {
612       case RS:
613         os() << "#RS" << nl;
614         break;
615       case RE:
616         os() << "#RE" << nl;
617         break;
618       case TAB:
619         os() << "#TAB" << nl;
620         break;
621       default:
622         os() << '#' << (unsigned long)*p << nl;
623         break;
624       }
625       p++;
626       length--;
627     }
628   }
629 }
630
631 int RastEventHandler::lexCmp(const StringC &s1, const StringC &s2)
632 {
633   const Char *p1 = s1.data();
634   size_t n1 = s1.size();
635   const Char *p2 = s2.data();
636   size_t n2 = s2.size();
637   for (;;) {
638     if (n1 == 0)
639       return n2 == 0 ? 0 : -1;
640     if (n2 == 0)
641       return 1;
642     if (*p1 != *p2) {
643       // printable characters precede non-printable characters;
644       // printable characters are in ASCII order
645       // non-printable characters are in document character set order
646       int a1 = printable(*p1);
647       int a2 = printable(*p2);
648       if (a1 == 0) {
649         if (a2 == 0)
650           return *p1 < *p2 ? -1 : 1;
651         else
652           return 1;
653       }
654       else if (a2 == 0)
655         return -1;
656       else
657         return a1 - a2;
658     }
659     p1++;
660     p2++;
661     n1--;
662     n2--;
663   }
664 }
665
666 void RastEventHandler::attributeInfo(const AttributeList &attributes,
667                                      AttributeType attributeType)
668 {
669   size_t length = attributes.size();
670   if (length == 0)
671     return;
672   size_t defIndex = attributes.defIndex();
673   if (defIndex >= attributeSortOrder_[attributeType].size())
674     attributeSortOrder_[attributeType].resize(defIndex + 1);
675   Vector<size_t> &sortOrder = attributeSortOrder_[attributeType][defIndex];
676   if (sortOrder.size() == 0
677       || attributeType == simpleAttribute) {
678     sortOrder.resize(length);
679     size_t i;
680     for (i = 0; i < length; i++)
681       sortOrder[i] = i;
682     // insertion sort
683     for (i = 1; i < length; i++) {
684       size_t tem = sortOrder[i];
685       size_t j;
686       for (j = i; j > 0; j--) {
687         if (lexCmp(attributes.name(sortOrder[j - 1]),
688                    attributes.name(tem)) <= 0)
689           break;
690         sortOrder[j] = sortOrder[j - 1];
691       }
692       sortOrder[j] = tem;
693     }
694   }
695   for (size_t j = 0; j < length; j++) {
696     // Don't use sortOrder because attributeSortOrder_ may be grown
697     // because of data attributes.
698     size_t i = attributeSortOrder_[attributeType][defIndex][j];
699     os() << attributes.name(i) << '=' << nl;
700     const Text *text;
701     const StringC *string;
702     const AttributeValue *value = attributes.value(i);
703     if (value) {
704       switch (value->info(text, string)) {
705       case AttributeValue::implied:
706         os() << "#IMPLIED" << nl;
707         break;
708       case AttributeValue::tokenized:
709         lines(markupLine, string->data(), string->size());
710         flushLine(markupLine);
711         break;
712       case AttributeValue::cdata:
713         {
714           TextIter iter(*text);
715           TextItem::Type type;
716           const Char *p;
717           size_t length;
718           const Location *loc;
719           while (iter.next(type, p, length, loc))
720             switch (type) {
721             case TextItem::data:
722             case TextItem::cdata:
723               lines(markupLine, p, length);
724               break;
725             case TextItem::sdata:
726               flushLine(markupLine);
727               os() << "#SDATA-TEXT" << nl;
728               lines(markupLine, p, length);
729               flushLine(markupLine);
730               os() << "#END-SDATA" << nl;
731               break;
732             default:
733               break;
734             }
735           flushLine(markupLine);
736         }
737         break;
738       }
739     }
740     const AttributeSemantics *semantics = attributes.semantics(i);
741     if (semantics) {
742       ConstPtr<Notation> notation
743         = semantics->notation();
744       if (!notation.isNull())
745         externalIdInfo(notation->externalId());
746       size_t nEntities = semantics->nEntities();
747       for (size_t i = 0; i < nEntities; i++) {
748         ConstPtr<Entity> entity
749           = semantics->entity(i);
750         if (!entity.isNull()) {
751           const ExternalDataEntity *externalDataEntity
752             = entity->asExternalDataEntity();
753           if (externalDataEntity)
754             externalEntityInfo(externalDataEntity,
755                                (attributeType == resultAttribute
756                                 ? resultAttribute
757                                 : dtdAttribute));
758           else {
759             const SubdocEntity *subdocEntity = entity->asSubdocEntity();
760             if (subdocEntity) {
761               Ptr<InputSourceOrigin> entityOrigin
762                 = new EntityOrigin(entity,
763                                    ((TokenizedAttributeValue *)value)
764                                    ->tokenLocation(i));
765               subdocEntityInfo(subdocEntity, entityOrigin, 0);
766             }
767             else {
768               const InternalEntity *internalEntity = entity->asInternalEntity();
769               if (internalEntity)
770                 internalEntityInfo(internalEntity);
771             }
772           }
773         }
774         os() << "#END-ENTITY" << nl;
775       }
776     }
777   }
778 }
779
780 void RastEventHandler::internalEntityInfo(const InternalEntity *entity)
781 {
782   if (!entity)
783     return;
784   os() << '#'
785        << char(entity->dataType() == Entity::cdata ? 'C' : 'S')
786        << "DATA-INTERNAL" << nl;
787   const StringC &str = entity->string();
788   lines(markupLine, str.data(), str.size());
789   flushLine(markupLine);
790 }
791
792 void RastEventHandler::endProlog(EndPrologEvent *event)
793 {
794   if (!event->lpdPointer().isNull()) {
795     linkProcess_.init(event->lpdPointer());
796     haveLinkProcess_ = 1;
797   }
798   if (event->simpleLinkNames().size() > 0)
799     endPrologEvent_ = event;
800   else
801     delete event;
802 }
803
804 void RastEventHandler::uselink(UselinkEvent *event)
805 {
806   linkProcess_.uselink(event->linkSet(),
807                        event->restore(),
808                        event->lpd().pointer());
809   if (haveLinkProcess_ && linkProcess_.nImpliedLinkRules() > 0) {
810     flushLine(dataLine);
811     os() << "#LINK-SET-INFO" << nl;
812     impliedSourceLinkRules();
813     os() << "#END-LINK-SET-INFO" << nl;
814   }
815   delete event;
816 }
817
818 void RastEventHandler::initMessage(Message &msg)
819 {
820   mgr_->initMessage(msg);
821 }
822
823 void RastEventHandler::dispatchMessage(Message &msg)
824 {
825   dispatchMessage((const Message &) msg);
826 }
827
828 void RastEventHandler::dispatchMessage(const Message &msg)
829 {
830   if (msg.isError())
831     piErrorCount_++;
832   if (!cancelled()) {
833     noteMessage(msg);
834     mgr_->dispatchMessage(msg);
835   }
836 }
837
838 RastLinkProcess::RastLinkProcess()
839 : rast_(0)
840 {
841 }
842
843 void RastLinkProcess::setHandler(RastEventHandler *rast)
844 {
845   rast_ = rast;
846 }
847
848 // Always return 1. 0 means not ready.
849
850 Boolean RastLinkProcess::selectLinkRule(const Vector<const AttributeList *> &linkAttributes,
851                                         const Location &location,
852                                         size_t &selected)
853 {
854   if (!rast_->linkRuleQueue_.empty()) {
855     LinkRulePi *p = rast_->linkRuleQueue_.get();
856     if (!selectLinkRulePi(p->pi, p->loc, linkAttributes, selected))
857       selected = 0;
858   }
859   else {
860     if (linkAttributes.size() > 0) {
861       rast_->setNextLocation(location);
862       rast_->Messenger::message(RastEventHandlerMessages::multipleLinkRules);
863     }
864     selected = 0;
865   }
866   return 1;
867 }
868
869 // Return zero for failure (RAST-PI-ERROR).
870
871 Boolean RastLinkProcess::selectLinkRulePi(const StringC &value,
872                                           const Location &loc,
873                                           const Vector<const AttributeList *> &linkAttributes,
874                                           size_t &selected)
875 {
876   Boolean haveSelection = 0;
877   size_t i;
878   for (i = 0; i < linkAttributes.size(); i++) {
879     const AttributeList &a = *linkAttributes[i];
880     Boolean matchValue = 0;
881     for (size_t j = 0; j < a.size(); j++) {
882       const Text *textp;
883       const StringC *strp;
884       switch (a.value(j)->info(textp, strp)) {
885       case AttributeValue::cdata:
886         // What if it contains SDATA entities?
887         if (textp->string() == value)
888           matchValue = 1;
889         break;
890       case AttributeValue::tokenized:
891         if (*strp == value)
892           matchValue = 1;
893         break;
894       default:
895         break;
896       }
897       if (matchValue)
898         break;
899     }
900     if (matchValue) {
901       if (haveSelection) {
902         rast_->setNextLocation(loc);
903         rast_->Messenger::message(RastEventHandlerMessages::multipleLinkRuleMatch);
904         return 0;
905       }
906       haveSelection = 1;
907       selected = i;
908     }
909   }
910   if (!haveSelection) {
911     rast_->setNextLocation(loc);
912     rast_->Messenger::message(RastEventHandlerMessages::noLinkRuleMatch);
913     return 0;
914   }
915   return 1;
916 }
917
918 void RastLinkProcess::swap(RastLinkProcess &to)
919 {
920   LinkProcess::swap(to);
921   RastEventHandler *tem = to.rast_;
922   to.rast_ = rast_;
923   rast_ = tem;
924 }
925
926 #ifdef SP_NAMESPACE
927 }
928 #endif