Logo Search packages:      
Sourcecode: jade version File versions  Download package

parseDecl.cxx

// Copyright (c) 1994, 1995 James Clark
// See the file COPYING for copying permission.

// Prolog, dtd and declaration parsing.

#include "splib.h"
#include "Parser.h"
#include "Param.h"
#include "Markup.h"
#include "ParserMessages.h"
#include "MessageArg.h"
#include "TokenMessageArg.h"
#include "token.h"
#include "macros.h"

#ifdef SP_NAMESPACE
namespace SP_NAMESPACE {
#endif

static const AllowedParams allowMdc(Param::mdc);
static const AllowedParams allowName(Param::name);
static const AllowedParams allowParamLiteral(Param::paramLiteral);
static const AllowedParams allowNameNameGroup(Param::name, Param::nameGroup);
static const AllowedParams allowDsoMdc(Param::dso, Param::mdc);
static AllowedParams allowNameMdc(Param::name, Param::mdc);
static AllowedParams
  allowExplicitLinkRuleMdc(Param::mdc,
                     Param::name,
                     Param::nameGroup,
                     Param::indicatedReservedName + Syntax::rIMPLIED);
static AllowedParams
  allowNameNameGroupMdc(Param::name, Param::nameGroup, Param::mdc);

static const AllowedParams
  allowLinkSetSpec(Param::name,
               Param::indicatedReservedName + Syntax::rINITIAL,
               Param::indicatedReservedName + Syntax::rEMPTY,
               Param::indicatedReservedName + Syntax::rRESTORE);

void Parser::doProlog()
{
  const unsigned maxTries = 10;
  unsigned tries = 0;
  do {
    if (cancelled()) {
      allDone();
      return;
    }
    Token token = getToken(proMode);
    switch (token) {
    case tokenUnrecognized:
      if (reportNonSgmlCharacter())
      break;
      if (hadDtd()) {
      currentInput()->ungetToken();
      endProlog();
      return;
      }
      {
      StringC gi;
      if (lookingAtStartTag(gi)) {
        currentInput()->ungetToken();
        implyDtd(gi);
        return;
      }
      }
          
      if (++tries >= maxTries) {
      message(ParserMessages::notSgml);
      giveUp();
      return;
      }
      message(ParserMessages::prologCharacter, StringMessageArg(currentToken()));
      prologRecover();
      break;
    case tokenEe:
      if (hadDtd()) {
      endProlog();
      return;
      }
      message(ParserMessages::documentEndProlog);
      allDone();
      return;
    case tokenMdoMdc:
      // empty comment
      emptyCommentDecl();
      break;
    case tokenMdoCom:
      if (!parseCommentDecl())
      prologRecover();
      break;
    case tokenMdoNameStart:
      setPass2Start();
      if (startMarkup(eventsWanted().wantPrologMarkup(), currentLocation()))
      currentMarkup()->addDelim(Syntax::dMDO);
      Syntax::ReservedName name;
      if (parseDeclarationName(&name)) {
      switch (name) {
      case Syntax::rDOCTYPE:
        if (!parseDoctypeDeclStart())
          giveUp();
        return;
      case Syntax::rLINKTYPE:
        if (!parseLinktypeDeclStart())
          giveUp();
        return;
      case Syntax::rELEMENT:
      case Syntax::rATTLIST:
      case Syntax::rENTITY:
      case Syntax::rNOTATION:
      case Syntax::rSHORTREF:
      case Syntax::rUSEMAP:
      case Syntax::rUSELINK:
      case Syntax::rLINK:
      case Syntax::rIDLINK:
        message(ParserMessages::prologDeclaration,
              StringMessageArg(syntax().reservedName(name)));
        if (!hadDtd())
          tries++;
        prologRecover();
        break;
      default:
        message(ParserMessages::noSuchDeclarationType,
              StringMessageArg(syntax().reservedName(name)));
        prologRecover();
        break;
      }
      }
      else
      prologRecover();
      break;
    case tokenPio:
      if (!parseProcessingInstruction())
      prologRecover();
      break;
    case tokenS:
      if (eventsWanted().wantPrologMarkup()) {
      extendS();
      eventHandler().sSep(new (eventAllocator())
                      SSepEvent(currentInput()->currentTokenStart(),
                              currentInput()->currentTokenLength(),
                              currentLocation(),
                              1));
      }
      break;
    default:
      CANNOT_HAPPEN();
    }
  } while (eventQueueEmpty());
}

void Parser::endProlog()
{
  if (baseDtd().isNull()
#if 0
      || baseDtd()->documentElementType()->definition()->undefined()
#endif
      ) {
    // We could continue, but there's not a lot of point.
    giveUp();
    return;
  }
  if (maybeStartPass2())
    setPhase(prologPhase);
  else {
    if (inputLevel() == 0) {
      allDone();
      return;
    }
    if (pass2())
      checkEntityStability();
    setPhase(instanceStartPhase);
    startInstance();
    ConstPtr<ComplexLpd> lpd;
    Vector<AttributeList> simpleLinkAtts;
    Vector<StringC> simpleLinkNames;
    for (size_t i = 0; i < nActiveLink(); i++)
      if (activeLpd(i).type() == Lpd::simpleLink) {
      const SimpleLpd &lpd = (SimpleLpd &)activeLpd(i);
      simpleLinkNames.push_back(lpd.name());
      simpleLinkAtts.resize(simpleLinkAtts.size() + 1);
      simpleLinkAtts.back().init(lpd.attributeDef());
      simpleLinkAtts.back().finish(*this);
      }
      else 
      lpd = (ComplexLpd *)&activeLpd(i);
    eventHandler().endProlog(new (eventAllocator())
                       EndPrologEvent(baseDtd(),
                                  lpd,
                                  simpleLinkNames,
                                  simpleLinkAtts,
                                  currentLocation()));
  }
}

void Parser::prologRecover()
{
  unsigned skipCount = 0;
  const unsigned skipMax = 250;
  for (;;) {
    Token token = getToken(proMode);
    skipCount++;
    if (token == tokenUnrecognized) {
      token = getToken(mdMode);
      if (token == tokenMdc) {
      token = getToken(proMode);
      if (token == tokenS)
        return;
      }
    }
    switch (token) {
    case tokenUnrecognized:
      (void)getChar();
      break;
    case tokenEe:
      return;
    case tokenMdoMdc:
    case tokenMdoCom:
    case tokenMdoNameStart:
    case tokenPio:
      currentInput()->ungetToken();
      return;
    case tokenS:
      if (currentChar() == syntax().standardFunction(Syntax::fRE)
        && skipCount >= skipMax)
      return;
    default:
      break;
    }
  }
}

void Parser::doDeclSubset()
{
  do {
    if (cancelled()) {
      allDone();
      return;
    }
    Token token = getToken(currentMode());
    unsigned startLevel = inputLevel();
    Boolean inDtd = !haveDefLpd();
    switch (token) {
    case tokenUnrecognized:
      if (reportNonSgmlCharacter())
      break;
      message(ParserMessages::declSubsetCharacter, StringMessageArg(currentToken()));
      declSubsetRecover(startLevel);
      break;
    case tokenEe:
      if (inputLevel() == specialParseInputLevel()) {
      // FIXME have separate messages for each type of special parse
      message(ParserMessages::specialParseEntityEnd);
      }
      if (eventsWanted().wantPrologMarkup())
      eventHandler().entityEnd(new (eventAllocator())
                         EntityEndEvent(currentLocation()));
      if (inputLevel() == 2) {
      const EntityDecl *e
        = currentLocation().origin()->entityDecl();
      if (e
          && (e->declType() == EntityDecl::doctype
            || e->declType() == EntityDecl::linktype)) {
        // popInputStack may destroy e
        Boolean fake = e->defLocation().origin().isNull();
        popInputStack();
        if (!(inDtd
            ? parseDoctypeDeclEnd(fake)
            : parseLinktypeDeclEnd()))
          ;             // FIXME recover
        setPhase(prologPhase);
        return;
      }
      }
      if (inputLevel() == 1) {
      if (finalPhase() == declSubsetPhase) {
        checkDtd(defDtd());
        endDtd();
      }
      else
        // Give message before popping stack.
        message(inDtd
              ? ParserMessages::documentEndDtdSubset
              : ParserMessages::documentEndLpdSubset);
      popInputStack();
      allDone();
      }
      else
      popInputStack();
      return;
    case tokenDsc:            // end of declaration subset
      // FIXME what's the right location?
      if (!referenceDsEntity(currentLocation())) {
      if (!(inDtd ? parseDoctypeDeclEnd() : parseLinktypeDeclEnd()))
        ;               // FIXME recover
      setPhase(prologPhase);
      }
      return;
    case tokenMdoNameStart:   // named markup declaration
      if (startMarkup(eventsWanted().wantPrologMarkup(), currentLocation()))
      currentMarkup()->addDelim(Syntax::dMDO);
      Syntax::ReservedName name;
      Boolean result;
      if (parseDeclarationName(&name,
                         inDtd && !options().errorAfdr)) {
      switch (name) {
      case Syntax::rANY:      // used for <!AFDR
        result = parseAfdrDecl();
        break;
      case Syntax::rELEMENT:
        if (inDtd)
          result = parseElementDecl();
        else {
          message(ParserMessages::lpdSubsetDeclaration,
                StringMessageArg(syntax().reservedName(name)));
          result = 0;
        }
        break;
      case Syntax::rATTLIST:
        result = parseAttlistDecl();
        break;
      case Syntax::rENTITY:
        result = parseEntityDecl();
        break;
      case Syntax::rNOTATION:
        result = parseNotationDecl();
        if (!inDtd && !sd().www())
          message(ParserMessages::lpdSubsetDeclaration,
                StringMessageArg(syntax().reservedName(name)));
        break;
      case Syntax::rSHORTREF:
        if (inDtd)
          result = parseShortrefDecl();
        else {
          message(ParserMessages::lpdSubsetDeclaration,
                StringMessageArg(syntax().reservedName(name)));
          result = 0;
        }
        break;
      case Syntax::rUSEMAP:
        if (inDtd)
          result = parseUsemapDecl();
        else {
          message(ParserMessages::lpdSubsetDeclaration,
                StringMessageArg(syntax().reservedName(name)));
          result = 0;
        }
        break;
      case Syntax::rLINK:
        if (inDtd) {
          message(ParserMessages::dtdSubsetDeclaration,
                StringMessageArg(syntax().reservedName(name)));
          result = 0;
        }
        else
          result = parseLinkDecl();
        break;
      case Syntax::rIDLINK:
        if (inDtd) {
          message(ParserMessages::dtdSubsetDeclaration,
                StringMessageArg(syntax().reservedName(name)));
          result = 0;
        }
        else
          result = parseIdlinkDecl();
        break;
      case Syntax::rDOCTYPE:
      case Syntax::rLINKTYPE:
      case Syntax::rUSELINK:
        result = 0;
        message(inDtd
              ? ParserMessages::dtdSubsetDeclaration
              : ParserMessages::lpdSubsetDeclaration,
              StringMessageArg(syntax().reservedName(name)));
        break;
      default:
        result = 0;
        message(ParserMessages::noSuchDeclarationType,
              StringMessageArg(syntax().reservedName(name)));
        break;
      }
      }
      else
      result = 0;
      if (!result)
      declSubsetRecover(startLevel);
      break;
    case tokenMdoMdc:         // empty comment declaration
      // empty comment
      emptyCommentDecl();
      break;
    case tokenMdoCom:         // comment declaration
      if (!parseCommentDecl())
      declSubsetRecover(startLevel);
      break;
    case tokenMdoDso:         // marked section declaration
      if (!parseMarkedSectionDeclStart())
      declSubsetRecover(startLevel);
      break;
    case tokenMscMdc:
      handleMarkedSectionEnd();
      break;
    case tokenPeroGrpo:       // parameter entity reference with name group
      message(ParserMessages::peroGrpoProlog);
      // fall through
    case tokenPeroNameStart:  // parameter entity reference
      {
      ConstPtr<Entity> entity;
      Ptr<EntityOrigin> origin;
      if (parseEntityReference(1, token == tokenPeroGrpo, entity, origin)) {
        if (!entity.isNull())
          entity->dsReference(*this, origin);
      }
      else
        declSubsetRecover(startLevel);
      }
      break;
    case tokenPio:            // processing instruction
      if (!parseProcessingInstruction())
      declSubsetRecover(startLevel);
      break;
    case tokenS:        // white space
      if (eventsWanted().wantPrologMarkup()) {
      extendS();
      eventHandler().sSep(new (eventAllocator())
                      SSepEvent(currentInput()->currentTokenStart(),
                              currentInput()->currentTokenLength(),
                              currentLocation(),
                              1));
      }
      break;
    case tokenIgnoredChar:
      // from an ignored marked section
      if (eventsWanted().wantPrologMarkup())
      eventHandler().ignoredChars(new (eventAllocator())
                            IgnoredCharsEvent(currentInput()->currentTokenStart(),
                                          currentInput()->currentTokenLength(),
                                          currentLocation(),
                                          1));
      break;
    case tokenRe:
    case tokenRs:
    case tokenCroNameStart:
    case tokenCroDigit:
    case tokenHcroHexDigit:
    case tokenEroNameStart:
    case tokenEroGrpo:
    case tokenChar:
      // these can occur in a cdata or rcdata marked section
      message(ParserMessages::dataMarkedSectionDeclSubset);
      declSubsetRecover(startLevel);
      break;
    default:
      CANNOT_HAPPEN();
    }
  } while (eventQueueEmpty());
}

void Parser::declSubsetRecover(unsigned startLevel)
{
  for (;;) {
    Token token = getToken(currentMode());
    switch (token) {
    case tokenUnrecognized:
      (void)getChar();
      break;
    case tokenEe:
      if (inputLevel() <= startLevel)
      return;
      popInputStack();
      break;
    case tokenMdoCom:
    case tokenDsc:
    case tokenMdoNameStart:
    case tokenMdoMdc:
    case tokenMdoDso:
    case tokenMscMdc:
    case tokenPio:
      if (inputLevel() == startLevel) {
      currentInput()->ungetToken();
      return;
      }
      break;
    default:
      break;
    }
  }
}

Boolean Parser::lookingAtStartTag(StringC &gi)
{
  // This is harder than might be expected since we may not have compiled
  // the recognizers for the instance yet.
  const StringC &stago = instanceSyntax().delimGeneral(Syntax::dSTAGO);
  for (size_t i = currentInput()->currentTokenLength();
       i < stago.size();
       i++)
    if (currentInput()->tokenChar(messenger()) == InputSource::eE)
      return 0;
  StringC delim;
  getCurrentToken(instanceSyntax().generalSubstTable(), delim);
  if (delim != stago)
    return 0;
  Xchar c = currentInput()->tokenChar(messenger());
  if (!instanceSyntax().isNameStartCharacter(c))
    return 0;
  do {
    gi += (*instanceSyntax().generalSubstTable())[(Char)c];
    c = currentInput()->tokenChar(messenger());
  } while (instanceSyntax().isNameCharacter(c));
  return 1;
}

Boolean Parser::parseDeclarationName(Syntax::ReservedName *result,
                             Boolean allowAfdr)
{
  currentInput()->discardInitial();
  extendNameToken(syntax().namelen(), ParserMessages::nameLength);
  StringC &name = nameBuffer();
  getCurrentToken(syntax().generalSubstTable(), name);
  if (!syntax().lookupReservedName(name, result)) {
    if (allowAfdr && name == sd().execToInternal("AFDR")) {
      *result = Syntax::rANY;
      if (currentMarkup())
      currentMarkup()->addName(currentInput());
    }
    else {
      message(ParserMessages::noSuchDeclarationType, StringMessageArg(name));
      return 0;
    }
  }
  else if (currentMarkup())
    currentMarkup()->addReservedName(*result, currentInput());
  return 1;
}

Boolean Parser::parseElementDecl()
{
  unsigned declInputLevel = inputLevel();
  Param parm;
  if (!parseParam(allowNameNameGroup, declInputLevel, parm))
    return 0;
  Vector<NameToken> nameVector;
  if (parm.type == Param::nameGroup) {
    parm.nameTokenVector.swap(nameVector);
    if (options().warnElementGroupDecl)
      message(ParserMessages::elementGroupDecl);
  }
  else {
    nameVector.resize(1);
    parm.token.swap(nameVector[0].name);
  }
  static AllowedParams
    allowRankOmissionContent(Param::number,
                       Param::reservedName + Syntax::rO,
                       Param::minus,
                       Param::reservedName + Syntax::rCDATA,
                       Param::reservedName + Syntax::rRCDATA,
                       Param::reservedName + Syntax::rEMPTY,
                       Param::reservedName + Syntax::rANY,
                       Param::modelGroup);
  if (!parseParam(allowRankOmissionContent, declInputLevel, parm))
    return 0;
  StringC rankSuffix;
  Vector<ElementType *> elements(nameVector.size());
  Vector<RankStem *> rankStems;
  Vector<const RankStem *> constRankStems;
  size_t i;
  if (parm.type == Param::number) {
    if (options().warnRank)
      message(ParserMessages::rank);
    parm.token.swap(rankSuffix);
    rankStems.resize(nameVector.size());
    constRankStems.resize(nameVector.size());
    for (i = 0; i < elements.size(); i++) {
      StringC name(nameVector[i].name);
      name += rankSuffix;
      if (name.size() > syntax().namelen()
        && nameVector[i].name.size() <= syntax().namelen())
      message(ParserMessages::genericIdentifierLength,
            NumberMessageArg(syntax().namelen()));
      elements[i] = lookupCreateElement(name);
      rankStems[i] = lookupCreateRankStem(nameVector[i].name);
      constRankStems[i] = rankStems[i];
    }
    static AllowedParams
      allowOmissionContent(Param::reservedName + Syntax::rO,
                     Param::minus,
                     Param::reservedName + Syntax::rCDATA,
                     Param::reservedName + Syntax::rRCDATA,
                     Param::reservedName + Syntax::rEMPTY,
                     Param::reservedName + Syntax::rANY,
                     Param::modelGroup);
    Token token = getToken(mdMinusMode);
    if (token == tokenNameStart)
      message(ParserMessages::psRequired);
    currentInput()->ungetToken();
    if (!parseParam(allowOmissionContent, declInputLevel, parm))
      return 0;
  }
  else {
    for (i = 0; i < elements.size(); i++)
      elements[i] = lookupCreateElement(nameVector[i].name);
  }
  for (i = 0; i < elements.size(); i++)
    if (defDtd().lookupRankStem(elements[i]->name()) && validate())
      message(ParserMessages::rankStemGenericIdentifier,
            StringMessageArg(elements[i]->name()));
  unsigned char omitFlags = 0;
  if (parm.type == Param::minus
      || parm.type == Param::reservedName + Syntax::rO) {
    if (options().warnMinimizationParam)
      message(ParserMessages::minimizationParam);
    omitFlags |= ElementDefinition::omitSpec;
    if (parm.type != Param::minus)
      omitFlags |= ElementDefinition::omitStart;
    static AllowedParams allowOmission(Param::reservedName + Syntax::rO,
                               Param::minus);
    if (!parseParam(allowOmission, declInputLevel, parm))
      return 0;
    if (parm.type != Param::minus)
      omitFlags |= ElementDefinition::omitEnd;
    static AllowedParams allowContent(Param::reservedName + Syntax::rCDATA,
                              Param::reservedName + Syntax::rRCDATA,
                              Param::reservedName + Syntax::rEMPTY,
                              Param::reservedName + Syntax::rANY,
                              Param::modelGroup);
    if (!parseParam(allowContent, declInputLevel, parm))
      return 0;
  }
  else {
    if (sd().omittag())
      message(ParserMessages::missingTagMinimization);
  }
  Ptr<ElementDefinition> def;
  switch (parm.type) {
  case Param::reservedName + Syntax::rCDATA:
    def = new ElementDefinition(markupLocation(),
                        defDtd().allocElementDefinitionIndex(),
                        omitFlags,
                        ElementDefinition::cdata);
    if (!parseParam(allowMdc, declInputLevel, parm))
      return 0;
    if (options().warnCdataContent)
      message(ParserMessages::cdataContent);
    break;
  case Param::reservedName + Syntax::rRCDATA:
    def = new ElementDefinition(markupLocation(),
                        defDtd().allocElementDefinitionIndex(),
                        omitFlags,
                        ElementDefinition::rcdata);
    if (!parseParam(allowMdc, declInputLevel, parm))
      return 0;
    if (options().warnRcdataContent)
      message(ParserMessages::rcdataContent);
    break;
  case Param::reservedName + Syntax::rEMPTY:
    def = new ElementDefinition(markupLocation(),
                        defDtd().allocElementDefinitionIndex(),
                        omitFlags,
                        ElementDefinition::empty);
    if ((omitFlags & ElementDefinition::omitSpec)
      && !(omitFlags & ElementDefinition::omitEnd)
      && options().warnShould)
      message(ParserMessages::emptyOmitEndTag);
    if (!parseParam(allowMdc, declInputLevel, parm))
      return 0;
    break;
  case Param::reservedName + Syntax::rANY:
    def = new ElementDefinition(markupLocation(),
                        defDtd().allocElementDefinitionIndex(),
                        omitFlags,
                        ElementDefinition::any);
    if (!parseExceptions(declInputLevel, def))
      return 0;
    break;
  case Param::modelGroup:
    {
      unsigned long cnt = parm.modelGroupPtr->grpgtcnt();
      // The outermost model group isn't formally a content token.
      if (cnt - 1 > syntax().grpgtcnt())
      message(ParserMessages::grpgtcnt, NumberMessageArg(syntax().grpgtcnt()));
      Owner<CompiledModelGroup>
      modelGroup(new CompiledModelGroup(parm.modelGroupPtr));
      Vector<ContentModelAmbiguity> ambiguities;
      Boolean pcdataUnreachable;
      modelGroup->compile(currentDtd().nElementTypeIndex(), ambiguities,
                    pcdataUnreachable);
      if (pcdataUnreachable && options().warnMixedContent)
      message(ParserMessages::pcdataUnreachable);
      if (validate()) {
      for (i = 0; i < ambiguities.size(); i++) {
        const ContentModelAmbiguity &a = ambiguities[i];
        reportAmbiguity(a.from, a.to1, a.to2, a.andDepth);
      }
      }
      def = new ElementDefinition(markupLocation(),
                          defDtd().allocElementDefinitionIndex(),
                          omitFlags,
                          ElementDefinition::modelGroup,
                          modelGroup);
      if (!parseExceptions(declInputLevel, def))
      return 0;
    }
    break;
  }
  if (rankSuffix.size() > 0)
    def->setRank(rankSuffix, constRankStems);
  ConstPtr<ElementDefinition> constDef(def);
  for (i = 0; i < elements.size(); i++) {
    if (elements[i]->definition() != 0) {
      if (validate())
      message(ParserMessages::duplicateElementDefinition,
              StringMessageArg(elements[i]->name()));
    }
    else {
      elements[i]->setElementDefinition(constDef, i);
      if (!elements[i]->attributeDef().isNull())
      checkElementAttribute(elements[i]);
    }
    if (rankStems.size() > 0)
      rankStems[i]->addDefinition(constDef);
  }
  if (currentMarkup()) {
    Vector<const ElementType *> v(elements.size());
    for (i = 0; i < elements.size(); i++)
      v[i] = elements[i];
    eventHandler().elementDecl(new (eventAllocator())
                         ElementDeclEvent(v, currentDtdPointer(),
                                    markupLocation(),
                                    currentMarkup()));
  }
  return 1;
}

void Parser::reportAmbiguity(const LeafContentToken *from,
                       const LeafContentToken *to1,
                       const LeafContentToken *to2,
                       unsigned ambigAndDepth)
{
  StringC toName;
  const ElementType *toType = to1->elementType();
  if (toType)
    toName = toType->name();
  else {
    toName = syntax().delimGeneral(Syntax::dRNI);
    toName += syntax().reservedName(Syntax::rPCDATA);
  }
  unsigned to1Index = to1->typeIndex() + 1;
  unsigned to2Index = to2->typeIndex() + 1;
  if (from->isInitial())
    message(ParserMessages::ambiguousModelInitial,
          StringMessageArg(toName),
          OrdinalMessageArg(to1Index),
          OrdinalMessageArg(to2Index));
  else {
    StringC fromName;
    const ElementType *fromType = from->elementType();
    if (fromType)
      fromName = fromType->name();
    else {
      fromName = syntax().delimGeneral(Syntax::dRNI);
      fromName += syntax().reservedName(Syntax::rPCDATA);
    }
    unsigned fromIndex = from->typeIndex() + 1;
    unsigned andMatches = from->andDepth() - ambigAndDepth;
    if (andMatches == 0)
      message(ParserMessages::ambiguousModel,
            StringMessageArg(fromName),
            OrdinalMessageArg(fromIndex),
            StringMessageArg(toName),
            OrdinalMessageArg(to1Index),
            OrdinalMessageArg(to2Index));
    else if (andMatches == 1)
      message(ParserMessages::ambiguousModelSingleAnd,
            StringMessageArg(fromName),
            OrdinalMessageArg(fromIndex),
            StringMessageArg(toName),
            OrdinalMessageArg(to1Index),
            OrdinalMessageArg(to2Index));
    else
      message(ParserMessages::ambiguousModelMultipleAnd,
            StringMessageArg(fromName),
            OrdinalMessageArg(fromIndex),
            NumberMessageArg(andMatches),
            StringMessageArg(toName),
            OrdinalMessageArg(to1Index),
            OrdinalMessageArg(to2Index));
  }
}


// Check the compatibility of the attribute definition with
// the element definition.

void Parser::checkElementAttribute(const ElementType *e, size_t checkFrom)
{
  if (!validate())
    return;
  const AttributeDefinitionList *attDef = e->attributeDef().pointer();
  Boolean conref = 0;
  ASSERT(e != 0);
  const ElementDefinition *edef = e->definition();
  ASSERT(edef != 0);
  ASSERT(attDef != 0);
  size_t attDefLength = attDef->size();
  for (size_t i = checkFrom; i < attDefLength; i++) {
    const AttributeDefinition *p = attDef->def(i);
    if (p->isConref())
      conref = 1;
    if (p->isNotation()
      && edef->declaredContent() == ElementDefinition::empty)
      message(ParserMessages::notationEmpty, StringMessageArg(e->name()));
  }
  if (conref) {
#if 0
    if (edef->omittedTagSpec() && !edef->canOmitEndTag()
      && options().warnShould)
      message(ParserMessages::conrefOmitEndTag, StringMessageArg(e->name()));
#endif
    if (edef->declaredContent() == ElementDefinition::empty)
      message(ParserMessages::conrefEmpty, StringMessageArg(e->name()));
  }
}

ElementType *Parser::lookupCreateElement(const StringC &name)
{
  ElementType *e = defDtd().lookupElementType(name);
  if (!e) {
    if (haveDefLpd()) 
      message(ParserMessages::noSuchSourceElement, StringMessageArg(name));
    else {
      e = new ElementType(name, defDtd().allocElementTypeIndex());
      defDtd().insertElementType(e);
    }
  }
  return e;
}

RankStem *Parser::lookupCreateRankStem(const StringC &name)
{
  RankStem *r = defDtd().lookupRankStem(name);
  if (!r) {
    r = new RankStem(name, defDtd().nRankStem());
    defDtd().insertRankStem(r);
    const ElementType *e = defDtd().lookupElementType(name);
    if (e && e->definition() != 0)
      message(ParserMessages::rankStemGenericIdentifier, StringMessageArg(name));
  }
  return r;
}

Boolean Parser::parseExceptions(unsigned declInputLevel,
                        Ptr<ElementDefinition> &def)
{
  Param parm;
  static AllowedParams
    allowExceptionsMdc(Param::mdc, Param::exclusions, Param::inclusions);
  if (!parseParam(allowExceptionsMdc, declInputLevel, parm))
    return 0;
  if (parm.type == Param::exclusions) {
    if (options().warnExclusion)
      message(ParserMessages::exclusion);
    def->setExclusions(parm.elementVector);
    static AllowedParams allowInclusionsMdc(Param::mdc, Param::inclusions);
    if (!parseParam(allowInclusionsMdc, declInputLevel, parm))
      return 0;
  }
  if (parm.type == Param::inclusions) {
    if (options().warnInclusion)
      message(ParserMessages::inclusion);
    def->setInclusions(parm.elementVector);
    size_t nI = def->nInclusions();
    size_t nE = def->nExclusions();
    if (nE) {
      for (size_t i = 0; i < nI; i++) {
      const ElementType *e = def->inclusion(i);
      for (size_t j = 0; j < nE; j++)
        if (def->exclusion(j) == e)
          message(ParserMessages::excludeIncludeSame,
                StringMessageArg(e->name()));
      }
    }
    if (!parseParam(allowMdc, declInputLevel, parm))
      return 0;
  }
  return 1;
}

Boolean Parser::parseAttlistDecl()
{
  unsigned declInputLevel = inputLevel();
  Param parm;
  size_t attcnt = 0;
  size_t idIndex = size_t(-1);
  size_t notationIndex = size_t(-1);
  Boolean anyCurrent = 0;
  
  Boolean isNotation;
  Vector<Attributed *> attributed;
  if (!parseAttributed(declInputLevel, parm, attributed, isNotation))
    return 0;
  Vector<CopyOwner<AttributeDefinition> > defs;
  if (!parseParam(sd().www() ? allowNameMdc : allowName, declInputLevel, parm))
    return 0;
  while (parm.type != Param::mdc) {
    StringC attributeName;
    parm.token.swap(attributeName);
    attcnt++;
    Boolean duplicate = 0;
    size_t i;
    for (i = 0; i < defs.size(); i++)
      if (defs[i]->name() == attributeName) {
      message(ParserMessages::duplicateAttributeDef,
            StringMessageArg(attributeName));
      duplicate = 1;
      break;
      }
    Owner<DeclaredValue> declaredValue;
    if (!parseDeclaredValue(declInputLevel, isNotation, parm, declaredValue))
      return 0;
    if (!duplicate) {
      if (declaredValue->isId()) {
      if (idIndex != size_t(-1))
        message(ParserMessages::multipleIdAttributes,
              StringMessageArg(defs[idIndex]->name()));
      idIndex = defs.size();
      }
      else if (declaredValue->isNotation()) {
      if (notationIndex != size_t(-1))
        message(ParserMessages::multipleNotationAttributes,
              StringMessageArg(defs[notationIndex]->name()));
      notationIndex = defs.size();
      }
    }
    const Vector<StringC> *tokensPtr = declaredValue->getTokens();
    if (tokensPtr) {
      size_t nTokens = tokensPtr->size();
      if (!sd().www()) {
      Vector<StringC>::const_iterator tokens = tokensPtr->begin();
      for (i = 0; i < nTokens; i++) {
        for (size_t j = 0; j < defs.size(); j++)
          if (defs[j]->containsToken(tokens[i])) {
            message(ParserMessages::duplicateAttributeToken,
                  StringMessageArg(tokens[i]));
            break;
          }
      }
      }
      attcnt += nTokens;
    }
    Owner<AttributeDefinition> def;
    if (!parseDefaultValue(declInputLevel, isNotation, parm, attributeName,
                     declaredValue, def, anyCurrent))
      return 0;
    if (haveDefLpd() && defLpd().type() == Lpd::simpleLink && !def->isFixed())
      message(ParserMessages::simpleLinkFixedAttribute);
    if (!duplicate) {
      defs.resize(defs.size() + 1);
      defs.back() = def.extract();
    }
    static AllowedParams allowNameMdc(Param::name, Param::mdc);
    if (!parseParam(allowNameMdc, declInputLevel, parm))
      return 0;
  }
  if (attcnt > syntax().attcnt())
    message(ParserMessages::attcnt,
          NumberMessageArg(attcnt),
          NumberMessageArg(syntax().attcnt()));
  if (haveDefLpd() && !isNotation) {
    if (defLpd().type() == Lpd::simpleLink) {
      for (size_t i = 0; i < attributed.size(); i++) {
      const ElementType *e = (const ElementType *)attributed[i];
      if (e) {
        if (e->name() == defLpd().sourceDtd()->name()) {
          SimpleLpd &lpd = (SimpleLpd &)defLpd();
          if (lpd.attributeDef().isNull())
            lpd.setAttributeDef(new AttributeDefinitionList(defs, 0));
          else
            message(ParserMessages::duplicateAttlistElement,
                  StringMessageArg(e->name()));
        }
        else
          message(ParserMessages::simpleLinkAttlistElement,
                StringMessageArg(e->name()));
      }
      }
    }
    else {
      Ptr<AttributeDefinitionList>
      adl(new AttributeDefinitionList(defs,
                              defComplexLpd()
                              .allocAttributeDefinitionListIndex()));
      for (size_t i = 0; i < attributed.size(); i++) {
      const ElementType *e = (const ElementType *)attributed[i];
      if (e) {
        if (defComplexLpd().attributeDef(e).isNull())
          defComplexLpd().setAttributeDef(e, adl);
        else
          message(ParserMessages::duplicateAttlistElement,
                StringMessageArg(e->name()));
      }
      }
    }
  }
  else {
    Ptr<AttributeDefinitionList>
      adl(new AttributeDefinitionList(defs,
                              defDtd()
                              .allocAttributeDefinitionListIndex(),
                              anyCurrent,
                              idIndex,
                              notationIndex));
    for (size_t i = 0; i < attributed.size(); i++) {
      if (attributed[i]->attributeDef().isNull()) {
      attributed[i]->setAttributeDef(adl);
      if (!isNotation) {
        ElementType *e = (ElementType *)attributed[i];
        if (e->definition() != 0)
          checkElementAttribute(e);
      }
      }
      else if (options().errorAfdr && !sd().www()) {
      if (isNotation)
        message(ParserMessages::duplicateAttlistNotation,
              StringMessageArg(((Notation *)attributed[i])->name()));
      else
        message(ParserMessages::duplicateAttlistElement,
              StringMessageArg(((ElementType *)attributed[i])->name()));
      }
      else {
      if (!hadAfdrDecl() && !sd().www()) {
        message(ParserMessages::missingAfdrDecl);
        setHadAfdrDecl();
      }
      AttributeDefinitionList *curAdl;
      {
        // Use block to make sure temporary gets destroyed.
        curAdl = attributed[i]->attributeDef().pointer();
      }
      size_t oldSize = curAdl->size();
      if (curAdl->count() != 1) {
        Vector<CopyOwner<AttributeDefinition> > copy(oldSize);
        for (size_t j = 0; j < oldSize; j++)
          copy[j] = curAdl->def(j)->copy();
        Ptr<AttributeDefinitionList> adlCopy
          = new AttributeDefinitionList(copy,
                                defDtd().allocAttributeDefinitionListIndex(),
                                curAdl->anyCurrent(),
                                curAdl->idIndex(),
                                curAdl->notationIndex());
        attributed[i]->setAttributeDef(adlCopy);
        curAdl = adlCopy.pointer();
      }
      // FIXME check for multiple ID and NOTATION attributes
      for (size_t j = 0; j < adl->size(); j++) {
        unsigned tem;
        if (!curAdl->attributeIndex(adl->def(j)->name(), tem))
          curAdl->append(adl->def(j)->copy());
      }
      if (!isNotation) {
        ElementType *e = (ElementType *)attributed[i];
        if (e->definition() != 0)
          checkElementAttribute(e, oldSize);
      }
      }
    }
  }
  if (currentMarkup()) {
    if (isNotation) {
      Vector<ConstPtr<Notation> > v(attributed.size());
      for (size_t i = 0; i < attributed.size(); i++)
      v[i] = (Notation *)attributed[i];
      eventHandler()
      .attlistNotationDecl(new (eventAllocator())
                       AttlistNotationDeclEvent(v,
                                          markupLocation(),
                                          currentMarkup()));
    }
    else {
      Vector<const ElementType *> v(attributed.size());
      for (size_t i = 0; i < attributed.size(); i++)
      v[i] = (ElementType *)attributed[i];
      if (haveDefLpd())
      eventHandler()
        .linkAttlistDecl(new (eventAllocator())
                     LinkAttlistDeclEvent(v,
                                    defLpdPointer(),
                                    markupLocation(),
                                    currentMarkup()));
      else
      eventHandler().attlistDecl(new (eventAllocator())
                           AttlistDeclEvent(v,
                                        currentDtdPointer(),
                                        markupLocation(),
                                        currentMarkup()));
    }
  }
  if (isNotation) {
    Dtd::EntityIter entityIter(defDtd().generalEntityIter());
    for (;;) {
      Ptr<Entity> entity(entityIter.next());
      if (entity.isNull())
      break;
      const ExternalDataEntity *external = entity->asExternalDataEntity();
      if (external) {
      const Notation *entityNotation = external->notation();
      for (size_t  i = 0; i < attributed.size(); i++)
        if ((Notation *)attributed[i] == entityNotation) {
          AttributeList attributes(entityNotation->attributeDef());
          attributes.finish(*this);
          ((ExternalDataEntity *)entity.pointer())
            ->setNotation((Notation *)attributed[i], attributes);
        }
      }
    }
  }
  return 1;
}


Boolean Parser::parseAttributed(unsigned declInputLevel,
                        Param &parm,
                        Vector<Attributed *> &attributed,
                        Boolean &isNotation)
{
  static AllowedParams
    allowNameGroupNotation(Param::name,
                     Param::nameGroup,
                     Param::indicatedReservedName + Syntax::rNOTATION);
  static AllowedParams
    allowNameGroupNotationAll(Param::name,
                        Param::nameGroup,
                        Param::indicatedReservedName
                        + Syntax::rNOTATION,
                        Param::indicatedReservedName
                        + Syntax::rALL,
                        Param::indicatedReservedName
                        + Syntax::rIMPLICIT);
  if (!parseParam(haveDefLpd()
              ? allowNameGroupNotation
              : allowNameGroupNotationAll,
              declInputLevel, parm))
    return 0;
  if (parm.type == Param::indicatedReservedName + Syntax::rNOTATION) {
    if (options().warnDataAttributes)
      message(ParserMessages::dataAttributes);
    isNotation = 1;
    static AllowedParams
      allowNameGroupAll(Param::name,
                  Param::nameGroup,
                  Param::indicatedReservedName + Syntax::rALL,
                  Param::indicatedReservedName + Syntax::rIMPLICIT);
    if (!parseParam(haveDefLpd()
                ? allowNameNameGroup
                : allowNameGroupAll,
                declInputLevel, parm))
      return 0;
    if (parm.type == Param::nameGroup) {
      attributed.resize(parm.nameTokenVector.size());
      for (size_t i = 0; i < attributed.size(); i++)
      attributed[i] = lookupCreateNotation(parm.nameTokenVector[i].name);
    }
    else {
      if (parm.type != Param::name && !hadAfdrDecl() && !sd().www()) {
      message(ParserMessages::missingAfdrDecl);
      setHadAfdrDecl();
      }
      attributed.resize(1);
      attributed[0]
      = lookupCreateNotation(parm.type == Param::name
                         ? parm.token
                         : syntax().rniReservedName(Syntax::ReservedName(parm.type - Param::indicatedReservedName)));
    }
  }
  else {
    isNotation = 0;
    if (parm.type == Param::nameGroup) {
      if (options().warnAttlistGroupDecl)
      message(ParserMessages::attlistGroupDecl);
      attributed.resize(parm.nameTokenVector.size());
      for (size_t i = 0; i < attributed.size(); i++)
      attributed[i] = lookupCreateElement(parm.nameTokenVector[i].name);
    }
    else {
      if (parm.type != Param::name && !hadAfdrDecl() && !sd().www()) {
      message(ParserMessages::missingAfdrDecl);
      setHadAfdrDecl();
      }
      attributed.resize(1);
      attributed[0]
      = lookupCreateElement(parm.type == Param::name
                        ? parm.token
                        : syntax().rniReservedName(Syntax::ReservedName(parm.type - Param::indicatedReservedName)));
    }
  }
  return 1;
}

Boolean Parser::parseDeclaredValue(unsigned declInputLevel,
                           Boolean isNotation,
                           Param &parm,
                           Owner<DeclaredValue> &declaredValue)
{
  static Param::Type declaredValues[] = {
    Param::reservedName + Syntax::rCDATA,
    Param::reservedName + Syntax::rENTITY,
    Param::reservedName + Syntax::rENTITIES,
    Param::reservedName + Syntax::rID,
    Param::reservedName + Syntax::rIDREF,
    Param::reservedName + Syntax::rIDREFS,
    Param::reservedName + Syntax::rNAME,
    Param::reservedName + Syntax::rNAMES,
    Param::reservedName + Syntax::rNMTOKEN,
    Param::reservedName + Syntax::rNMTOKENS,
    Param::reservedName + Syntax::rNUMBER,
    Param::reservedName + Syntax::rNUMBERS,
    Param::reservedName + Syntax::rNUTOKEN,
    Param::reservedName + Syntax::rNUTOKENS,
    Param::reservedName + Syntax::rNOTATION,
    Param::nameTokenGroup
    };
  static AllowedParams allowDeclaredValue(declaredValues,
                                SIZEOF(declaredValues));
  if (!parseParam(allowDeclaredValue, declInputLevel, parm))
    return 0;
  enum { asDataAttribute = 01, asLinkAttribute = 02 };
  unsigned allowedFlags = asDataAttribute|asLinkAttribute;
  switch (parm.type) {
  case Param::reservedName + Syntax::rCDATA:
    declaredValue = new CdataDeclaredValue;
    break;
  case Param::reservedName + Syntax::rENTITY:
    declaredValue = new EntityDeclaredValue(0);
    allowedFlags = asLinkAttribute;
    break;
  case Param::reservedName + Syntax::rENTITIES:
    declaredValue = new EntityDeclaredValue(1);
    allowedFlags = asLinkAttribute;
    break;
  case Param::reservedName + Syntax::rID:
    declaredValue = new IdDeclaredValue;
    allowedFlags = 0;
    break;
  case Param::reservedName + Syntax::rIDREF:
    declaredValue = new IdrefDeclaredValue(0);
    allowedFlags = 0;
    break;
  case Param::reservedName + Syntax::rIDREFS:
    declaredValue = new IdrefDeclaredValue(1);
    allowedFlags = 0;
    break;
  case Param::reservedName + Syntax::rNAME:
    declaredValue
      = new TokenizedDeclaredValue(TokenizedDeclaredValue::name, 0);
    if (options().warnNameDeclaredValue)
      message(ParserMessages::nameDeclaredValue);
    break;
  case Param::reservedName + Syntax::rNAMES:
    declaredValue
      = new TokenizedDeclaredValue(TokenizedDeclaredValue::name, 1);
    if (options().warnNameDeclaredValue)
      message(ParserMessages::nameDeclaredValue);
    break;
  case Param::reservedName + Syntax::rNMTOKEN:
    declaredValue
      = new TokenizedDeclaredValue(TokenizedDeclaredValue::nameToken, 0);
    break;
  case Param::reservedName + Syntax::rNMTOKENS:
    declaredValue
      = new TokenizedDeclaredValue(TokenizedDeclaredValue::nameToken, 1);
    break;
  case Param::reservedName + Syntax::rNUMBER:
    declaredValue
      = new TokenizedDeclaredValue(TokenizedDeclaredValue::number, 0);
    if (options().warnNumberDeclaredValue)
      message(ParserMessages::numberDeclaredValue);
    break;
  case Param::reservedName + Syntax::rNUMBERS:
    declaredValue
      = new TokenizedDeclaredValue(TokenizedDeclaredValue::number, 1);
    if (options().warnNumberDeclaredValue)
      message(ParserMessages::numberDeclaredValue);
    break;
  case Param::reservedName + Syntax::rNUTOKEN:
    declaredValue
      = new TokenizedDeclaredValue(TokenizedDeclaredValue::numberToken, 0);
    if (options().warnNutokenDeclaredValue)
      message(ParserMessages::nutokenDeclaredValue);
    break;
  case Param::reservedName + Syntax::rNUTOKENS:
    declaredValue
      = new TokenizedDeclaredValue(TokenizedDeclaredValue::numberToken, 1);
    if (options().warnNutokenDeclaredValue)
      message(ParserMessages::nutokenDeclaredValue);
    break;
  case Param::reservedName + Syntax::rNOTATION:
    {
      static AllowedParams allowNameGroup(Param::nameGroup);
      if (!parseParam(allowNameGroup, declInputLevel, parm))
      return 0;
      Vector<StringC> group(parm.nameTokenVector.size());
      for (size_t i = 0; i < group.size(); i++)
      parm.nameTokenVector[i].name.swap(group[i]);
      declaredValue = new NotationDeclaredValue(group);
      allowedFlags = 0;
    }
    break;
  case Param::nameTokenGroup:
    {
      Vector<StringC> group(parm.nameTokenVector.size());
      for (size_t i = 0; i < group.size(); i++)
      parm.nameTokenVector[i].name.swap(group[i]);
      declaredValue = new NameTokenGroupDeclaredValue(group);
    }
    break;
  default:
    CANNOT_HAPPEN();
  }
  if (isNotation) {
    if (!(allowedFlags & asDataAttribute))
      message(ParserMessages::dataAttributeDeclaredValue);
  }
  else if (haveDefLpd() && !isNotation && !(allowedFlags & asLinkAttribute))
    message(ParserMessages::linkAttributeDeclaredValue);
  return 1;
}

Boolean Parser::parseDefaultValue(unsigned declInputLevel,
                          Boolean isNotation,
                          Param &parm,
                          const StringC &attributeName,
                          Owner<DeclaredValue> &declaredValue,
                          Owner<AttributeDefinition> &def,
                          Boolean &anyCurrent)
{
  // default value
  static AllowedParams
    allowDefaultValue(Param::indicatedReservedName + Syntax::rFIXED,
                  Param::indicatedReservedName + Syntax::rREQUIRED,
                  Param::indicatedReservedName + Syntax::rCURRENT,
                  Param::indicatedReservedName + Syntax::rCONREF,
                  Param::indicatedReservedName + Syntax::rIMPLIED,
                  Param::attributeValue,
                  Param::attributeValueLiteral);
  static AllowedParams
    allowTokenDefaultValue(Param::indicatedReservedName + Syntax::rFIXED,
                  Param::indicatedReservedName + Syntax::rREQUIRED,
                  Param::indicatedReservedName + Syntax::rCURRENT,
                  Param::indicatedReservedName + Syntax::rCONREF,
                  Param::indicatedReservedName + Syntax::rIMPLIED,
                  Param::attributeValue,
                  Param::tokenizedAttributeValueLiteral);
  if (!parseParam(declaredValue->tokenized()
              ? allowTokenDefaultValue
              : allowDefaultValue, declInputLevel, parm))
    return 0;
  switch (parm.type) {
  case Param::indicatedReservedName + Syntax::rFIXED:
    {
      static AllowedParams allowValue(Param::attributeValue,
                              Param::attributeValueLiteral);
      static AllowedParams
      allowTokenValue(Param::attributeValue,
                  Param::tokenizedAttributeValueLiteral);
      if (!parseParam(declaredValue->tokenized()
                  ? allowTokenValue
                  : allowValue, declInputLevel, parm))
      return 0;
      unsigned specLength = 0;
      AttributeValue *value = declaredValue->makeValue(parm.literalText,
                                           *this,
                                           attributeName,
                                           specLength);
      if (declaredValue->isId())
      message(ParserMessages::idDeclaredValue);
      def = new FixedAttributeDefinition(attributeName,
                               declaredValue.extract(),
                               value);
    }
    break;
  case Param::attributeValue:
    if (options().warnAttributeValueNotLiteral)
      message(ParserMessages::attributeValueNotLiteral);
    // falll through
  case Param::attributeValueLiteral:
  case Param::tokenizedAttributeValueLiteral:
    {
      unsigned specLength = 0;
      AttributeValue *value = declaredValue->makeValue(parm.literalText,
                                           *this,
                                           attributeName,
                                           specLength);
      if (declaredValue->isId())
      message(ParserMessages::idDeclaredValue);
      def = new DefaultAttributeDefinition(attributeName,
                                 declaredValue.extract(),
                                 value);
    }
    break;
  case Param::indicatedReservedName + Syntax::rREQUIRED:
    def = new RequiredAttributeDefinition(attributeName,
                                declaredValue.extract());
    break;
  case Param::indicatedReservedName + Syntax::rCURRENT:
    anyCurrent = 1;            
    if (declaredValue->isId())
      message(ParserMessages::idDeclaredValue);
    def = new CurrentAttributeDefinition(attributeName,
                               declaredValue.extract(),
                               defDtd().allocCurrentAttributeIndex());
    if (isNotation)
      message(ParserMessages::dataAttributeDefaultValue);
    else if (haveDefLpd())
      message(ParserMessages::linkAttributeDefaultValue);
    else if (options().warnCurrent)
      message(ParserMessages::currentAttribute);
    break;
  case Param::indicatedReservedName + Syntax::rCONREF:
    if (declaredValue->isId())
      message(ParserMessages::idDeclaredValue);
    def = new ConrefAttributeDefinition(attributeName,
                              declaredValue.extract());
    if (isNotation)
      message(ParserMessages::dataAttributeDefaultValue);
    else if (haveDefLpd())
      message(ParserMessages::linkAttributeDefaultValue);
    else if (options().warnConref)
      message(ParserMessages::conrefAttribute);
    break;
  case Param::indicatedReservedName + Syntax::rIMPLIED:
    def = new ImpliedAttributeDefinition(attributeName,
                               declaredValue.extract());
    break;
  default:
    CANNOT_HAPPEN();
  }
  return 1;
}

// parm contains either system or public

Boolean Parser::parseExternalId(const AllowedParams &sysidAllow,
                        const AllowedParams &endAllow,
                        Boolean maybeWarnMissingSystemId,
                        unsigned declInputLevel,
                        Param &parm,
                        ExternalId &id)
{
  id.setLocation(currentLocation());
  if (parm.type == Param::reservedName + Syntax::rPUBLIC) {
    static AllowedParams allowMinimumLiteral(Param::minimumLiteral);
    if (!parseParam(allowMinimumLiteral, declInputLevel, parm))
      return 0;
    const MessageType1 *err;
    if (id.setPublic(parm.literalText, sd().internalCharset(), syntax().space(),
                  err)) {
      PublicId::TextClass textClass;
      if (sd().formal() && id.publicId()->getTextClass(textClass) && textClass == PublicId::SD)
      message(ParserMessages::wwwRequired);
    }
    else if (sd().formal())
      message(*err,
            StringMessageArg(*id.publicIdString()));
  }
  if (!parseParam(sysidAllow, declInputLevel, parm))
    return 0;
  if (parm.type == Param::systemIdentifier) {
    id.setSystem(parm.literalText);
    if (!parseParam(endAllow, declInputLevel, parm))
      return 0;
  }
  else if (options().warnMissingSystemId && maybeWarnMissingSystemId)
    message(ParserMessages::missingSystemId);
  return 1;
}

Boolean Parser::parseNotationDecl()
{
  unsigned declInputLevel = inputLevel();
  Param parm;
  if (!parseParam(allowName, declInputLevel, parm))
    return 0;
  Notation *nt = lookupCreateNotation(parm.token);
  if (validate() && nt->defined())
    message(ParserMessages::duplicateNotationDeclaration,
          StringMessageArg(parm.token));
  static AllowedParams
    allowPublicSystem(Param::reservedName + Syntax::rPUBLIC,
                  Param::reservedName + Syntax::rSYSTEM);
  if (!parseParam(allowPublicSystem, declInputLevel, parm))
    return 0;


  static AllowedParams allowSystemIdentifierMdc(Param::systemIdentifier,
                                    Param::mdc);

  ExternalId id;
  if (!parseExternalId(allowSystemIdentifierMdc, allowMdc,
                   parm.type == Param::reservedName + Syntax::rSYSTEM,
                   declInputLevel, parm, id))
    return 0;
  if (validate() && sd().formal()) {
    PublicId::TextClass textClass;
    const PublicId *publicId = id.publicId();
    if (publicId
      && publicId->getTextClass(textClass)
      && textClass != PublicId::NOTATION)
      message(ParserMessages::notationIdentifierTextClass);
  }
  if (!nt->defined()) {
    nt->setExternalId(id, markupLocation());
    nt->generateSystemId(*this);
    if (currentMarkup())
      eventHandler().notationDecl(new (eventAllocator())
                          NotationDeclEvent(nt, markupLocation(),
                                        currentMarkup()));
  }
  return 1;
}

Boolean Parser::parseEntityDecl()
{
  unsigned declInputLevel = inputLevel();
  Param parm;

  static AllowedParams
    allowEntityNamePero(Param::entityName,
                  Param::indicatedReservedName + Syntax::rDEFAULT,
                  Param::pero);

  if (!parseParam(allowEntityNamePero, declInputLevel, parm))
    return 0;

  Entity::DeclType declType;
  StringC name;               // empty for default entity
  if (parm.type == Param::pero) {
    declType = Entity::parameterEntity;
    static AllowedParams allowParamEntityName(Param::paramEntityName);
    if (!parseParam(allowParamEntityName, declInputLevel, parm))
      return 0;
    parm.token.swap(name);
  }
  else {
    declType = Entity::generalEntity;
    if (parm.type == Param::entityName)
      parm.token.swap(name);
    else if (options().warnDefaultEntityDecl)
      message(ParserMessages::defaultEntityDecl);
  }
  static AllowedParams
    allowEntityTextType(Param::paramLiteral,
                  Param::reservedName + Syntax::rCDATA,
                  Param::reservedName + Syntax::rSDATA,
                  Param::reservedName + Syntax::rPI,
                  Param::reservedName + Syntax::rSTARTTAG,
                  Param::reservedName + Syntax::rENDTAG,
                  Param::reservedName + Syntax::rMS,
                  Param::reservedName + Syntax::rMD,
                  Param::reservedName + Syntax::rSYSTEM,
                  Param::reservedName + Syntax::rPUBLIC);

  if (!parseParam(allowEntityTextType, declInputLevel, parm))
    return 0;
  Location typeLocation(currentLocation());
  Entity::DataType dataType = Entity::sgmlText;
  InternalTextEntity::Bracketed bracketed = InternalTextEntity::none;
  switch (parm.type) {
  case Param::reservedName + Syntax::rSYSTEM:
  case Param::reservedName + Syntax::rPUBLIC:
    return parseExternalEntity(name, declType, declInputLevel, parm);
  case Param::reservedName + Syntax::rCDATA:
    dataType = Entity::cdata;
    if (options().warnInternalCdataEntity)
      message(ParserMessages::internalCdataEntity);
    break;
  case Param::reservedName + Syntax::rSDATA:
    dataType = Entity::sdata;
    if (options().warnInternalSdataEntity)
      message(ParserMessages::internalSdataEntity);
    break;
  case Param::reservedName + Syntax::rPI:
    dataType = Entity::pi;
    if (options().warnPiEntity)
      message(ParserMessages::piEntity);
    break;
  case Param::reservedName + Syntax::rSTARTTAG:
    bracketed = InternalTextEntity::starttag;
    if (options().warnBracketEntity)
      message(ParserMessages::bracketEntity);
    break;
  case Param::reservedName + Syntax::rENDTAG:
    bracketed = InternalTextEntity::endtag;
    if (options().warnBracketEntity)
      message(ParserMessages::bracketEntity);
    break;
  case Param::reservedName + Syntax::rMS:
    bracketed = InternalTextEntity::ms;
    if (options().warnBracketEntity)
      message(ParserMessages::bracketEntity);
    break;
  case Param::reservedName + Syntax::rMD:
    bracketed = InternalTextEntity::md;
    if (options().warnBracketEntity)
      message(ParserMessages::bracketEntity);
    break;
  }
  if (parm.type != Param::paramLiteral) {
    if (!parseParam(allowParamLiteral, declInputLevel, parm))
      return 0;
  }
  Text text;
  parm.literalText.swap(text);
  if (bracketed != InternalTextEntity::none) {
    StringC open;
    StringC close;
    switch (bracketed) {
    case InternalTextEntity::starttag:
      open = syntax().delimGeneral(Syntax::dSTAGO);
      close = syntax().delimGeneral(Syntax::dTAGC);
      break;
    case InternalTextEntity::endtag:
      open = syntax().delimGeneral(Syntax::dETAGO);
      close = syntax().delimGeneral(Syntax::dTAGC);
      break;
    case InternalTextEntity::ms:
      open = syntax().delimGeneral(Syntax::dMDO);
      open += syntax().delimGeneral(Syntax::dDSO);
      close = syntax().delimGeneral(Syntax::dMSC);
      close += syntax().delimGeneral(Syntax::dMDC);
      break;
    case InternalTextEntity::md:
      open = syntax().delimGeneral(Syntax::dMDO);
      close = syntax().delimGeneral(Syntax::dMDC);
      break;
    default:
      CANNOT_HAPPEN();
    }
    text.insertChars(open, Location(new BracketOrigin(typeLocation,
                                          BracketOrigin::open),
                            0));
    text.addChars(close, Location(new BracketOrigin(typeLocation,
                                        BracketOrigin::close),
                          0));
    if (text.size() > syntax().litlen()
      && text.size() - open.size() - close.size() <= syntax().litlen())
      message(ParserMessages::bracketedLitlen,
            NumberMessageArg(syntax().litlen()));
  }
  if (!parseParam(allowMdc, declInputLevel, parm))
    return 0;
  if (declType == Entity::parameterEntity
      && (dataType == Entity::cdata || dataType == Entity::sdata)) {
    message(ParserMessages::internalParameterDataEntity,
          StringMessageArg(name));
    return 1;
  }
  Ptr<Entity> entity;
  switch (dataType) {
  case Entity::cdata:
    entity = new InternalCdataEntity(name, markupLocation(), text);
    break;
  case Entity::sdata:
    entity = new InternalSdataEntity(name, markupLocation(), text);
    break;
  case Entity::pi:
    entity = new PiEntity(name, declType, markupLocation(), text);
    break;
  case Entity::sgmlText:
    entity = new InternalTextEntity(name, declType, markupLocation(), text, bracketed);
    break;
  default:
    CANNOT_HAPPEN();
    break;
  }
  maybeDefineEntity(entity);
  return 1;
}

Boolean Parser::parseExternalEntity(StringC &name,
                            Entity::DeclType declType,
                            unsigned declInputLevel,
                            Param &parm)
{
  static AllowedParams
    allowSystemIdentifierEntityTypeMdc(Param::systemIdentifier,
                               Param::reservedName + Syntax::rSUBDOC,
                               Param::reservedName + Syntax::rCDATA,
                               Param::reservedName + Syntax::rSDATA,
                               Param::reservedName + Syntax::rNDATA,
                               Param::mdc);
  static AllowedParams
    allowEntityTypeMdc(Param::reservedName + Syntax::rSUBDOC,
                   Param::reservedName + Syntax::rCDATA,
                   Param::reservedName + Syntax::rSDATA,
                   Param::reservedName + Syntax::rNDATA,
                   Param::mdc);
  
  ExternalId id;
  if (!parseExternalId(allowSystemIdentifierEntityTypeMdc, allowEntityTypeMdc,
                   1, declInputLevel, parm, id))
    return 0;
  if (parm.type == Param::mdc) {
    maybeDefineEntity(new ExternalTextEntity(name, declType, markupLocation(),
                                   id));
    return 1;
  }
  Ptr<Entity> entity;
  if (parm.type == Param::reservedName + Syntax::rSUBDOC) {
    if (sd().subdoc() == 0)
      message(ParserMessages::subdocEntity, StringMessageArg(name));
    if (!parseParam(allowMdc, declInputLevel, parm))
      return 0;
    entity = new SubdocEntity(name, markupLocation(), id);
  }
  else {
    Entity::DataType dataType;
    switch (parm.type) {
    case Param::reservedName + Syntax::rCDATA:
      dataType = Entity::cdata;
      if (options().warnExternalCdataEntity)
      message(ParserMessages::externalCdataEntity);
      break;
    case Param::reservedName + Syntax::rSDATA:
      dataType = Entity::sdata;
      if (options().warnExternalSdataEntity)
      message(ParserMessages::externalSdataEntity);
      break;
    case Param::reservedName + Syntax::rNDATA:
      dataType = Entity::ndata;
      break;
    default:
      CANNOT_HAPPEN();
    }
    if (!parseParam(allowName, declInputLevel, parm))
      return 0;
    Ptr<Notation> notation(lookupCreateNotation(parm.token));
    if (!parseParam(allowDsoMdc, declInputLevel, parm))
      return 0;
    AttributeList attributes(notation->attributeDef());
    if (parm.type == Param::dso) {
      if (attributes.size() == 0)
      message(ParserMessages::notationNoAttributes,
            StringMessageArg(notation->name()));
      Boolean netEnabling;
      Ptr<AttributeDefinitionList> newAttDef;
      if (!parseAttributeSpec(1, attributes, netEnabling, newAttDef))
      return 0;
      if (!newAttDef.isNull()) {
      newAttDef->setIndex(defDtd().allocAttributeDefinitionListIndex());
      notation->setAttributeDef(newAttDef);
      }
      if (attributes.nSpec() == 0)
      message(ParserMessages::emptyDataAttributeSpec);
      if (!parseParam(allowMdc, declInputLevel, parm))
      return 0;
    }
    else
      attributes.finish(*this);
    entity = new ExternalDataEntity(name, dataType, markupLocation(), id, notation,
                            attributes);
  }
  if (declType == Entity::parameterEntity) {
    message(ParserMessages::externalParameterDataSubdocEntity,
          StringMessageArg(name));
    return 1;
  }
  maybeDefineEntity(entity);
  return 1;
}

Notation *Parser::lookupCreateNotation(const StringC &name)
{
  Ptr<Notation> nt = defDtd().lookupNotation(name);
  if (nt.isNull()) {
    nt = new Notation(name, defDtd().namePointer(), defDtd().isBase());
    defDtd().insertNotation(nt);
  }
  return nt.pointer();
}

void Parser::maybeDefineEntity(const Ptr<Entity> &entity)
{
  Dtd &dtd = defDtd();
  if (haveDefLpd())
    entity->setDeclIn(dtd.namePointer(),
                  dtd.isBase(),
                  defLpd().namePointer(),
                  defLpd().active());
  else
    entity->setDeclIn(dtd.namePointer(), dtd.isBase());
  Boolean ignored = 0;
  if (entity->name().size() == 0) {
    const Entity *oldEntity = dtd.defaultEntity().pointer();
    if (oldEntity == 0
      || (!oldEntity->declInActiveLpd() && entity->declInActiveLpd()))
      dtd.setDefaultEntity(entity, *this);
    else {
      ignored = 1;
      if (options().warnDuplicateEntity)
      message(ParserMessages::duplicateEntityDeclaration,
            StringMessageArg(syntax().rniReservedName(Syntax::rDEFAULT)));
    }
  }
  else {
    Ptr<Entity> oldEntity = dtd.insertEntity(entity);
    if (oldEntity.isNull())
      entity->generateSystemId(*this);
    else if (oldEntity->defaulted()) {
      dtd.insertEntity(entity, 1);
      message(ParserMessages::defaultedEntityDefined,
            StringMessageArg(entity->name()));
      entity->generateSystemId(*this);
    }
    else {
      if (entity->declInActiveLpd() && !oldEntity->declInActiveLpd()) {
      dtd.insertEntity(entity, 1);
      entity->generateSystemId(*this);
      }
      else {
      ignored = 1;
      if (options().warnDuplicateEntity)
        message(entity->declType() == Entity::parameterEntity
              ? ParserMessages::duplicateParameterEntityDeclaration
              : ParserMessages::duplicateEntityDeclaration,
              StringMessageArg(entity->name()));
      }
    }
  }
  if (currentMarkup())
    eventHandler().entityDecl(new (eventAllocator())
                        EntityDeclEvent(entity, ignored,
                                    markupLocation(),
                                    currentMarkup()));
}

Boolean Parser::parseShortrefDecl()
{
  if (!defDtd().isBase())
    message(ParserMessages::shortrefOnlyInBaseDtd);

  unsigned declInputLevel = inputLevel();
  Param parm;

  if (!parseParam(allowName, declInputLevel, parm))
    return 0;
  ShortReferenceMap *map = lookupCreateMap(parm.token);
  int valid = 1;
  if (map->defined()) {
    message(ParserMessages::duplicateShortrefDeclaration,
          StringMessageArg(parm.token),
          map->defLocation());
    valid = 0;
  }
  else
    map->setDefLocation(markupLocation());
  if (!parseParam(allowParamLiteral, declInputLevel, parm))
    return 0;
  Vector<StringC> vec;
  do {
    StringC delim(parm.literalText.string());
    const SubstTable<Char> *table = instanceSyntax().generalSubstTable();
    for (size_t i = 0; i < delim.size(); i++)
      table->subst(delim[i]);
    size_t srIndex;
    if (!defDtd().shortrefIndex(delim, instanceSyntax(), srIndex)) {
      message(ParserMessages::unknownShortrefDelim,
            StringMessageArg(prettifyDelim(delim)));
      valid = 0;
    }
    static AllowedParams allowEntityName(Param::entityName);
    if (!parseParam(allowEntityName, declInputLevel, parm))
      return 0;
    if (valid) {
      if (srIndex >= vec.size())
      vec.resize(srIndex + 1);
      if (vec[srIndex].size() > 0) {
      message(ParserMessages::delimDuplicateMap,
            StringMessageArg(prettifyDelim(delim)));
      valid = 0;
      }
      else
      parm.token.swap(vec[srIndex]);
    }
    static AllowedParams allowParamLiteralMdc(Param::paramLiteral, Param::mdc);
    if (!parseParam(allowParamLiteralMdc, declInputLevel, parm))
      return 0;
  } while (parm.type != Param::mdc);
  if (valid) {
    map->setNameMap(vec);
    if (currentMarkup())
      eventHandler().shortrefDecl(new (eventAllocator())
                          ShortrefDeclEvent(map,
                                        currentDtdPointer(),
                                        markupLocation(),
                                        currentMarkup()));
  }
  return 1;
}

StringC Parser::prettifyDelim(const StringC &delim)
{
  StringC prettyDelim;
  for (size_t i = 0; i < delim.size(); i++) {
    const StringC *nameP;
    if (syntax().charFunctionName(delim[i], nameP)) {
      prettyDelim += syntax().delimGeneral(Syntax::dCRO);
      prettyDelim += *nameP;
      prettyDelim += syntax().delimGeneral(Syntax::dREFC);
    }
    else
      prettyDelim += delim[i];
  }
  return prettyDelim;
}

ShortReferenceMap *Parser::lookupCreateMap(const StringC &name)
{
  ShortReferenceMap *map = defDtd().lookupShortReferenceMap(name);
  if (!map) {
    map = new ShortReferenceMap(name);
    defDtd().insertShortReferenceMap(map);
  }
  return map;
}

Boolean Parser::parseUsemapDecl()
{
  if (!inInstance() && !defDtd().isBase())
    message(ParserMessages::usemapOnlyInBaseDtd);

  unsigned declInputLevel = inputLevel();
  Param parm;
  static AllowedParams
    allowNameEmpty(Param::name,
               Param::indicatedReservedName + Syntax::rEMPTY);
  if (!parseParam(allowNameEmpty, declInputLevel, parm))
    return 0;
  const ShortReferenceMap *map;
  if (parm.type == Param::name) {
    if (inInstance()) {
      map = currentDtd().lookupShortReferenceMap(parm.token);
      if (!map)
      message(ParserMessages::undefinedShortrefMapInstance,
            StringMessageArg(parm.token));
    }
    else {
      ShortReferenceMap *tem = lookupCreateMap(parm.token);
      tem->setUsed();
      map = tem;
    }
  }
  else
    map = &theEmptyMap;
  static AllowedParams
    allowNameNameGroupMdc(Param::name, Param::nameGroup, Param::mdc);
  if (!parseParam(allowNameNameGroupMdc, declInputLevel, parm))
    return 0;
  if (parm.type != Param::mdc) {
    if (inInstance()) {
      message(ParserMessages::usemapAssociatedElementTypeInstance);
      if (!parseParam(allowMdc, declInputLevel, parm))
      return 0;
    }
    else {
      Vector<const ElementType *> v;
      if (parm.type == Param::name) {
      ElementType *e = lookupCreateElement(parm.token);
      v.push_back(e);
      if (!e->map())
        e->setMap(map);
      }
      else {
      v.resize(parm.nameTokenVector.size());
      for (size_t i = 0; i < parm.nameTokenVector.size(); i++) {
        ElementType *e
          = lookupCreateElement(parm.nameTokenVector[i].name);
        v[i] = e;
        if (!e->map())
          e->setMap(map);
      }
      }
      if (!parseParam(allowMdc, declInputLevel, parm))
      return 0;
      if (currentMarkup())
      eventHandler().usemap(new (eventAllocator())
                        UsemapEvent(map, v,
                                currentDtdPointer(),
                                markupLocation(),
                                currentMarkup()));
    }
  }
  else {
    if (!inInstance())
      message(ParserMessages::usemapAssociatedElementTypeDtd);
    else if (map) {
      if (map != &theEmptyMap && !map->defined())
      message(ParserMessages::undefinedShortrefMapInstance,
            StringMessageArg(map->name()));
      else {
      if (currentMarkup()) {
        Vector<const ElementType *> v;
        eventHandler().usemap(new (eventAllocator())
                        UsemapEvent(map, v,
                                  currentDtdPointer(),
                                  markupLocation(),
                                  currentMarkup()));
      }
      currentElement().setMap(map);
      }
    }
  }
  return 1;
}

Boolean Parser::parseDoctypeDeclStart()
{
  if (hadDtd() && !sd().concur() && !sd().explicitLink())
    message(ParserMessages::multipleDtds);
  if (hadLpd())
    message(ParserMessages::dtdAfterLpd);
  unsigned declInputLevel = inputLevel();
  Param parm;
  
  if (!parseParam(allowName, declInputLevel, parm))
    return 0;
  StringC name;
  parm.token.swap(name);
  if (!lookupDtd(name).isNull())
    message(ParserMessages::duplicateDtd, StringMessageArg(name));
  static AllowedParams
    allowPublicSystemDsoMdc(Param::reservedName + Syntax::rPUBLIC,
                      Param::reservedName + Syntax::rSYSTEM,
                      Param::dso,
                      Param::mdc);
  if (!parseParam(allowPublicSystemDsoMdc, declInputLevel, parm))
    return 0;
  ConstPtr<Entity> entity;
  if (parm.type == Param::reservedName + Syntax::rPUBLIC
      || parm.type == Param::reservedName + Syntax::rSYSTEM) {
    static AllowedParams allowSystemIdentifierDsoMdc(Param::systemIdentifier,
                                         Param::dso, Param::mdc);
    ExternalId id;
    if (!parseExternalId(allowSystemIdentifierDsoMdc, allowDsoMdc,
                   1, declInputLevel, parm, id))
      return 0;
    Ptr<Entity> tem
      = new ExternalTextEntity(name, Entity::doctype, markupLocation(), id);
    tem->generateSystemId(*this);
    entity = tem;
#if 0
    eventHandler()
      .externalEntityDecl(new (eventAllocator())
                    ExternalEntityDeclEvent(entity, 0));
#endif
  }
  else if (parm.type == Param::mdc) {
    if (!sd().implydefElement()) {
      message(ParserMessages::noDtdSubset);
      enableImplydef();
    }
  }
  // Discard mdc or dso
  if (currentMarkup())
    currentMarkup()->resize(currentMarkup()->size() - 1);
  eventHandler().startDtd(new (eventAllocator())
                    StartDtdEvent(name, entity, parm.type == Param::dso,
                              markupLocation(),
                              currentMarkup()));
  startDtd(name);
  if (parm.type == Param::mdc) {
    // unget the mdc
    currentInput()->ungetToken();
    if (entity.isNull()) {
      (void)parseDoctypeDeclEnd();
      return 1;
    }
    // reference the entity
    Ptr<EntityOrigin> origin
      = EntityOrigin::make(internalAllocator(), entity, currentLocation());
    entity->dsReference(*this, origin);
    if (inputLevel() == 1) {  // reference failed
      (void)parseDoctypeDeclEnd();
      return 1;
    }
  }
  else if (!entity.isNull())
    setDsEntity(entity);
  setPhase(declSubsetPhase);
  return 1;
}

void Parser::implyDtd(const StringC &gi)
{
  startMarkup(eventsWanted().wantPrologMarkup(), Location());
#if 0
  if (currentMarkup()) {
    currentMarkup()->addDelim(Syntax::dMDO);
    currentMarkup()->addReservedName(Syntax::rDOCTYPE,
                             syntax().reservedName(Syntax::rDOCTYPE));
    currentMarkup()->addS(syntax().space());
    currentMarkup()->addName(gi.data(), gi.size());
  }
#endif
  if (sd().implydefElement() && !sd().implydefDoctype()) {
    eventHandler().startDtd(new (eventAllocator())
                          StartDtdEvent(gi, ConstPtr<Entity>(), 0,
                              markupLocation(),
                              currentMarkup()));
    startDtd(gi);
    parseDoctypeDeclEnd(1);
    return;
  }
  ExternalId id;
  // The null location indicates that this is a fake entity.
  Entity *tem = new ExternalTextEntity(gi, Entity::doctype, Location(), id);
  ConstPtr<Entity> entity(tem);
  if (sd().implydefDoctype())
    tem->generateSystemId(*this);
  else {
    // Don't use Entity::generateSystemId because we don't want an error
    // if it fails.
    StringC str;
    if (!entityCatalog().lookup(*entity, syntax(), sd().internalCharset(),
                          messenger(), str)) {
      message(ParserMessages::noDtd);
      enableImplydef();
      eventHandler().startDtd(new (eventAllocator())
                           StartDtdEvent(gi, ConstPtr<Entity>(), 0,
                                         markupLocation(),
                                       currentMarkup()));
      startDtd(gi);
      parseDoctypeDeclEnd(1);
      return;
    }
    id.setEffectiveSystem(str);
#if 0
    if (currentMarkup()) {
      currentMarkup()->addS(syntax().space());
      currentMarkup()->addReservedName(Syntax::rSYSTEM,
                               syntax().reservedName(Syntax::rSYSTEM));
    }
#endif
    entity = new ExternalTextEntity(gi,
                            Entity::doctype,
                            Location(),
                            id);
    StringC declStr;
    declStr += syntax().delimGeneral(Syntax::dMDO);
    declStr += syntax().reservedName(Syntax::rDOCTYPE);
    declStr += syntax().space();
    declStr += gi;
    declStr += syntax().space();
    declStr += syntax().reservedName(Syntax::rSYSTEM);
    declStr += syntax().delimGeneral(Syntax::dMDC);
    message(ParserMessages::implyingDtd, StringMessageArg(declStr));
  }
  Ptr<EntityOrigin> origin
    = EntityOrigin::make(internalAllocator(), entity, currentLocation());
  eventHandler().startDtd(new (eventAllocator())
                    StartDtdEvent(gi, entity, 0,
                              markupLocation(),
                              currentMarkup()));
  startDtd(gi);
  entity->dsReference(*this, origin);
  if (inputLevel() == 1)
    parseDoctypeDeclEnd(1);
  else
    setPhase(declSubsetPhase);
}

Boolean Parser::parseDoctypeDeclEnd(Boolean fake)
{
  checkDtd(defDtd());
  Ptr<Dtd> tem(defDtdPointer());
  endDtd();
  if (fake) {
    startMarkup(eventsWanted().wantPrologMarkup(), Location());
#if 0
    if (currentMarkup())
      currentMarkup()->addDelim(Syntax::dMDC);
#endif
  }
  else {
    startMarkup(eventsWanted().wantPrologMarkup(), currentLocation());
    Param parm;
    // End DTD before parsing final param so parameter entity reference
    // not allowed between ] and >.
    if (!parseParam(allowMdc, inputLevel(), parm))
      return 0;
  }
  eventHandler().endDtd(new (eventAllocator()) EndDtdEvent(tem,
                                             markupLocation(),
                                             currentMarkup()));
#if 0
  if (fake) {
    Char c = syntax().standardFunction(Syntax::fRE);
    eventHandler().sSep(new (eventAllocator())
                  SSepEvent(&c, 1, Location(), 1));
  }
#endif
  return 1;
}

void Parser::checkDtd(Dtd &dtd)
{
  if (dtd.isBase())
    addNeededShortrefs(dtd, instanceSyntax());
  if (sd().www() || !options().errorAfdr)
    addCommonAttributes(dtd);
  Dtd::ElementTypeIter elementIter(dtd.elementTypeIter());
  ElementType *p;
  ConstPtr<ElementDefinition> def;
  int i = 0;
  while ((p = elementIter.next()) != 0) {
    if (p->definition() == 0) {
      if (p->name() == dtd.name()) {
      if (validate() && !implydefElement())
        message(ParserMessages::documentElementUndefined);
      }
      else if (options().warnUndefinedElement)
      message(ParserMessages::dtdUndefinedElement, StringMessageArg(p->name()));
      if (def.isNull())
      def = new ElementDefinition(currentLocation(),
                            size_t(ElementDefinition::undefinedIndex),
                            0,
                            ElementDefinition::any);
      p->setElementDefinition(def, i++);
    }
    const ShortReferenceMap *map = p->map();
    if (map != 0 && map != &theEmptyMap && !map->defined()) {
      if (validate())
      message(ParserMessages::undefinedShortrefMapDtd,
              StringMessageArg(map->name()),
              StringMessageArg(p->name()));
      p->setMap(0);
    }
  }
  Dtd::ShortReferenceMapIter mapIter(dtd.shortReferenceMapIter());
  int nShortref = dtd.nShortref();
  for (;;) {
    ShortReferenceMap *map = mapIter.next();
    if (!map)
      break;
    Vector<ConstPtr<Entity> > entityMap(nShortref);
    for (i = 0; i < nShortref; i++) {
      const StringC *entityName = map->entityName(i);
      if (entityName) {
      ConstPtr<Entity> entity
        = lookupEntity(0, *entityName, map->defLocation(), 0);
      if (entity.isNull()) {
        setNextLocation(map->defLocation());
        message(ParserMessages::mapEntityUndefined,
              StringMessageArg(*entityName),
              StringMessageArg(map->name()));
      }
      else {
        if (entity->defaulted() && options().warnDefaultEntityReference) {
          setNextLocation(map->defLocation());
          message(ParserMessages::mapDefaultEntity,
                StringMessageArg(*entityName),
                StringMessageArg(map->name()));
        }
        entityMap[i] = entity;
      }
      }
    }
    map->setEntityMap(entityMap);
    if (options().warnUnusedMap && !map->used()) {
      setNextLocation(map->defLocation());
      message(ParserMessages::unusedMap, StringMessageArg(map->name()));
    }
  }
  if (options().warnUnusedParam) {
    Dtd::ConstEntityIter entityIter(((const Dtd &)dtd).parameterEntityIter());
    for (;;) {
      ConstPtr<Entity> entity(entityIter.next());
      if (entity.isNull())
      break;
      if (!entity->used() && !maybeStatusKeyword(*entity)) {
      setNextLocation(entity->defLocation());
      message(ParserMessages::unusedParamEntity,
            StringMessageArg(entity->name()));
      }
    }
  }
  if (!validate())
    return;
  Dtd::ConstEntityIter entityIter(((const Dtd &)dtd).generalEntityIter());
  for (;;) {
    ConstPtr<Entity> entity(entityIter.next());
    if (entity.isNull())
      break;
    const ExternalDataEntity *external = entity->asExternalDataEntity();
    if (external) {
      const Notation *notation = external->notation();
      if (!notation->defined()) {
      setNextLocation(external->defLocation());
      message(ParserMessages::entityNotationUndefined,
            StringMessageArg(notation->name()),
            StringMessageArg(external->name()));
      }
    }
  }
  Dtd::NotationIter notationIter(dtd.notationIter());
  for (;;) {
    ConstPtr<Notation> notation(notationIter.next());
    if (notation.isNull())
      break;
    if (!notation->defined() && !notation->attributeDef().isNull())
      message(ParserMessages::attlistNotationUndefined,
            StringMessageArg(notation->name()));
  }
}

void Parser::addCommonAttributes(Dtd &dtd)
{
  Ptr<AttributeDefinitionList> commonAdl[2];
  {
    ElementType *e = dtd.removeElementType(syntax()
                                 .rniReservedName(Syntax::rALL));
    if (e) {
      commonAdl[0] = e->attributeDef();
      delete e;
      lookupCreateElement(syntax().rniReservedName(Syntax::rIMPLICIT));
    }
  }
  {
    Ptr<Notation> allNotation
      = dtd.removeNotation(syntax().rniReservedName(Syntax::rALL));
    if (!allNotation.isNull()) {
      commonAdl[1] = allNotation->attributeDef();
      lookupCreateNotation(syntax().rniReservedName(Syntax::rIMPLICIT));
    }
  }
  Dtd::ElementTypeIter elementIter(dtd.elementTypeIter());
  Dtd::NotationIter notationIter(dtd.notationIter());
  Vector<PackedBoolean> doneAdl(dtd.nAttributeDefinitionList(),
                        PackedBoolean(0));
  for (int isNotation = 0; isNotation < 2; isNotation++) {
    if (!commonAdl[isNotation].isNull()) {
      doneAdl[commonAdl[isNotation]->index()] = 1;
      for (;;) {
      Attributed *a;
      if (!isNotation)
        a = elementIter.next();
      else
        a = notationIter.next().pointer();
      if (!a)
        break;
      Ptr<AttributeDefinitionList> adl = a->attributeDef();
      if (adl.isNull())
        a->setAttributeDef(commonAdl[isNotation]);
      else if (!doneAdl[adl->index()]) {
        doneAdl[adl->index()] = 1;
        for (size_t j = 0; j < commonAdl[isNotation]->size(); j++) {
          unsigned tem;
          if (!adl->attributeIndex(commonAdl[isNotation]->def(j)->name(),
                             tem))
            adl->append(commonAdl[isNotation]->def(j)->copy());
        }
      }
      }
    }
  }
  {
    ElementType *e = dtd.removeElementType(syntax()
                                 .rniReservedName(Syntax::rIMPLICIT));
    if (e)
      dtd.setImplicitElementAttributeDef(e->attributeDef());
    delete e;
  }
  {
    Ptr<Notation> n
      = dtd.removeNotation(syntax().rniReservedName(Syntax::rIMPLICIT));
    if (!n.isNull())
      dtd.setImplicitNotationAttributeDef(n->attributeDef());
  }
}

Boolean Parser::maybeStatusKeyword(const Entity &entity)
{
  const InternalEntity *internal = entity.asInternalEntity();
  if (!internal)
    return 0;
  const StringC &text = internal->string();
  static const Syntax::ReservedName statusKeywords[] = {
    Syntax::rINCLUDE, Syntax::rIGNORE
    };
  for (size_t i = 0; i < SIZEOF(statusKeywords); i++) {
    const StringC &keyword = instanceSyntax().reservedName(statusKeywords[i]);
    size_t j = 0;
    while (j < text.size() && instanceSyntax().isS(text[j]))
      j++;
    size_t k = 0;
    while (j < text.size()
         && k < keyword.size()
         && ((*instanceSyntax().generalSubstTable())[text[j]]
             == keyword[k]))
      j++, k++;
    if (k == keyword.size()) {
      while (j < text.size() && instanceSyntax().isS(text[j]))
      j++;
      if (j == text.size())
      return 1;
    }
  }
  return 0;
}

Boolean Parser::parseLinktypeDeclStart()
{
  if (baseDtd().isNull())
    message(ParserMessages::lpdBeforeBaseDtd);
  unsigned declInputLevel = inputLevel();
  Param parm;
  
  if (!parseParam(allowName, declInputLevel, parm))
    return 0;
  StringC name;
  parm.token.swap(name);
  if (!lookupDtd(name).isNull())
    message(ParserMessages::duplicateDtdLpd, StringMessageArg(name));
  else if (!lookupLpd(name).isNull())
    message(ParserMessages::duplicateLpd, StringMessageArg(name));
  static AllowedParams
    allowSimpleName(Param::indicatedReservedName + Syntax::rSIMPLE,
                Param::name);
  if (!parseParam(allowSimpleName, declInputLevel, parm))
    return 0;
  Boolean simple;
  Ptr<Dtd> sourceDtd;
  if (parm.type == Param::indicatedReservedName + Syntax::rSIMPLE) {
    simple = 1;
    sourceDtd = baseDtd();
    if (sourceDtd.isNull())
      sourceDtd = new Dtd(StringC(), 1);
  }
  else {
    simple = 0;
    sourceDtd = lookupDtd(parm.token);
    if (sourceDtd.isNull()) {
      message(ParserMessages::noSuchDtd, StringMessageArg(parm.token));
      sourceDtd = new Dtd(parm.token, 0);
    }
  }
  static AllowedParams
    allowImpliedName(Param::indicatedReservedName + Syntax::rIMPLIED,
                 Param::name);
  if (!parseParam(allowImpliedName, declInputLevel, parm))
    return 0;
  Ptr<Dtd> resultDtd;
  Boolean implied = 0;
  if (parm.type == Param::indicatedReservedName + Syntax::rIMPLIED) {
    if (simple) {
      if (!sd().simpleLink())
      message(ParserMessages::simpleLinkFeature);
    }
    else {
      implied = 1;
      if (!sd().implicitLink())
      message(ParserMessages::implicitLinkFeature);
    }
  }
  else {
    if (simple)
      message(ParserMessages::simpleLinkResultNotImplied);
    else {
      if (!sd().explicitLink())
      message(ParserMessages::explicitLinkFeature);
      resultDtd = lookupDtd(parm.token);
      if (resultDtd.isNull())
      message(ParserMessages::noSuchDtd, StringMessageArg(parm.token));
    }
  }
  static AllowedParams
    allowPublicSystemDsoMdc(Param::reservedName + Syntax::rPUBLIC,
                      Param::reservedName + Syntax::rSYSTEM,
                      Param::dso,
                      Param::mdc);
  if (!parseParam(allowPublicSystemDsoMdc, declInputLevel, parm))
    return 0;
  ConstPtr<Entity> entity;
  if (parm.type == Param::reservedName + Syntax::rPUBLIC
      || parm.type == Param::reservedName + Syntax::rSYSTEM) {
    static AllowedParams allowSystemIdentifierDsoMdc(Param::systemIdentifier,
                                         Param::dso, Param::mdc);
    ExternalId id;
    if (!parseExternalId(allowSystemIdentifierDsoMdc, allowDsoMdc,
                   1, declInputLevel, parm, id))
      return 0;
    Ptr<Entity> tem
      = new ExternalTextEntity(name, Entity::linktype, markupLocation(), id);
    tem->generateSystemId(*this);
    entity = tem;
#if 0
    eventHandler()
      .externalEntityDecl(new (eventAllocator())
                    ExternalEntityDeclEvent(entity, 0));
#endif
  }
  Ptr<Lpd> lpd;
  if (simple)
    lpd = new SimpleLpd(name, markupLocation(), sourceDtd);
  else
    lpd = new ComplexLpd(name,
                   implied ? Lpd::implicitLink : Lpd::explicitLink,
                   markupLocation(),
                   syntax(),
                   sourceDtd,
                   resultDtd);
  if (!baseDtd().isNull() && shouldActivateLink(name)) {
    size_t nActive = nActiveLink();
    if (simple) {
      size_t nSimple = 0;
      for (size_t i = 0; i < nActive; i++)
      if (activeLpd(i).type() == Lpd::simpleLink)
        nSimple++;
      if (nSimple == sd().simpleLink())
      message(ParserMessages::simpleLinkCount,
            NumberMessageArg(sd().simpleLink()));
      lpd->activate();
    }
    else {
      Boolean haveImplicit = 0;
      Boolean haveExplicit = 0;
      size_t i;
      for (i = 0; i < nActive; i++) {
      if (activeLpd(i).type() == Lpd::implicitLink)
        haveImplicit = 1;
      else if (activeLpd(i).type() == Lpd::explicitLink)
        haveExplicit = 1;
      }
      const Dtd *sourceDtd = lpd->sourceDtd().pointer();
      if (implied && haveImplicit)
      message(ParserMessages::oneImplicitLink);
      else if (sd().explicitLink() <= 1 && sourceDtd != baseDtd().pointer())
      message(sd().explicitLink() == 0
            ? ParserMessages::explicitNoRequiresSourceTypeBase
            : ParserMessages::explicit1RequiresSourceTypeBase,
            StringMessageArg(lpd->name()));
      else if (sd().explicitLink() == 1 && haveExplicit && !implied)
      message(ParserMessages::duplicateExplicitChain);
      else if (haveExplicit || haveImplicit
             || sourceDtd != baseDtd().pointer())
      message(ParserMessages::sorryLink, StringMessageArg(lpd->name()));
      else
      lpd->activate();
    }
  }
  // Discard mdc or dso
  if (currentMarkup())
    currentMarkup()->resize(currentMarkup()->size() - 1);
  eventHandler().startLpd(new (eventAllocator())
                    StartLpdEvent(lpd->active(),
                              name,
                              entity,
                              parm.type == Param::dso,
                              markupLocation(),
                              currentMarkup()));
  startLpd(lpd);
  if (parm.type == Param::mdc) {
    // unget the mdc
    currentInput()->ungetToken();
    if (entity.isNull()) {
      message(ParserMessages::noLpdSubset, StringMessageArg(name));
      (void)parseLinktypeDeclEnd();
      return 1;
    }
    // reference the entity
    Ptr<EntityOrigin> origin
      = EntityOrigin::make(internalAllocator(), entity, currentLocation());
    entity->dsReference(*this, origin);
    if (inputLevel() == 1) {  // reference failed
      (void)parseLinktypeDeclEnd();
      return 1;
    }
  }
  else if (!entity.isNull())
    setDsEntity(entity);
  setPhase(declSubsetPhase);
  return 1;
}

Boolean Parser::parseLinktypeDeclEnd()
{

  if (defLpd().type() != Lpd::simpleLink) {
    if (!defComplexLpd().initialLinkSet()->defined())
      message(ParserMessages::noInitialLinkSet,
            StringMessageArg(defLpd().name()));
    ComplexLpd::ConstLinkSetIter iter = defComplexLpd().linkSetIter();
    const LinkSet *linkSet;
    while ((linkSet = iter.next()) != 0)
      if (!linkSet->defined())
      message(ParserMessages::undefinedLinkSet, StringMessageArg(linkSet->name()));
  }
  ConstPtr<Lpd> tem(defLpdPointer());
  endLpd();
  startMarkup(eventsWanted().wantPrologMarkup(), currentLocation());
  Param parm;
  Boolean result = parseParam(allowMdc, inputLevel(), parm);
  eventHandler().endLpd(new (eventAllocator()) EndLpdEvent(tem,
                                             markupLocation(),
                                             currentMarkup()));
  return result;
}

Boolean Parser::parseLinkDecl()
{
  return parseLinkSet(0);
}

Boolean Parser::parseIdlinkDecl()
{
  return parseLinkSet(1);
}

// This will only get called if we're defining a complex lpd.

Boolean Parser::parseLinkSet(Boolean idlink)
{
  if (defLpd().type() == Lpd::simpleLink) {
    message(idlink ? ParserMessages::idlinkDeclSimple : ParserMessages::linkDeclSimple);
    return 0;
  }
  if (idlink) {
    if (defComplexLpd().hadIdLinkSet())
      message(ParserMessages::duplicateIdLinkSet);
    else
      defComplexLpd().setHadIdLinkSet();
  }
  unsigned declInputLevel = inputLevel();
  Param parm;
  
  Boolean isExplicit = (defLpd().type() == Lpd::explicitLink);
  LinkSet *linkSet;
  if (idlink) {
    if (!parseParam(allowName, declInputLevel, parm))
      return 0;
    linkSet = 0;
  }
  else {
    static AllowedParams
      allowNameInitial(Param::name,
                   Param::indicatedReservedName + Syntax::rINITIAL);
    if (!parseParam(allowNameInitial, declInputLevel, parm))
      return 0;
    if (parm.type == Param::name)
      linkSet = lookupCreateLinkSet(parm.token);
    else
      linkSet = defComplexLpd().initialLinkSet();
    if (linkSet->defined())
      message(ParserMessages::duplicateLinkSet, StringMessageArg(linkSet->name()));
    static AllowedParams
      allowExplicitLinkRule(Param::name,
                      Param::nameGroup,
                      Param::indicatedReservedName + Syntax::rIMPLIED);
    if (!parseParam(isExplicit ? allowExplicitLinkRule : allowNameNameGroup,
                declInputLevel, parm))
      return 0;
  }

  do {
    StringC id;
    if (idlink) {
      parm.token.swap(id);
      if (!parseParam(isExplicit ? allowExplicitLinkRuleMdc : allowNameNameGroupMdc,
                  declInputLevel, parm))
      return 0;
    }
    if (parm.type == Param::indicatedReservedName + Syntax::rIMPLIED) {
      if (!parseParam(allowName, declInputLevel, parm))
      return 0;
      Boolean resultImplied;
      const ElementType *resultType;
      AttributeList resultAttributes;
      if (!parseResultElementSpec(declInputLevel,
                          parm,
                          idlink,
                          resultImplied,
                          resultType,
                          resultAttributes))
      return 0;
      if (resultType) {
      const AttributeList *dummy;
      if (linkSet->impliedResultAttributes(resultType, dummy))
        message(ParserMessages::duplicateImpliedResult,
              StringMessageArg(resultType->name()));
      else
        linkSet->addImplied(resultType, resultAttributes);
      }
    }
    else {
      SourceLinkRule *linkRule = 0;
      IdLinkRule idLinkRule;
      Ptr<SourceLinkRuleResource> linkRuleResource;
      if (idlink)
      linkRule = &idLinkRule;
      else {
      linkRuleResource = new SourceLinkRuleResource;
      linkRule = linkRuleResource.pointer();
      }
      Vector<const ElementType *> assocElementTypes;
      if (parm.type == Param::name) {
      assocElementTypes.resize(1);
      assocElementTypes[0] = lookupCreateElement(parm.token);
      }
      else {
      assocElementTypes.resize(parm.nameTokenVector.size());
      for (size_t i = 0; i < assocElementTypes.size(); i++)
        assocElementTypes[i]
          = lookupCreateElement(parm.nameTokenVector[i].name);
      }
      static AllowedParams
      allow2i(Param::indicatedReservedName + Syntax::rUSELINK,
            Param::indicatedReservedName + Syntax::rPOSTLINK,
            Param::dso,
            Param::mdc,
            Param::name,
            Param::nameGroup);
      static AllowedParams
      allow2id(Param::indicatedReservedName + Syntax::rUSELINK,
             Param::indicatedReservedName + Syntax::rPOSTLINK,
             Param::dso,
             Param::mdc,
             Param::name);
      static AllowedParams
      allow2e(Param::indicatedReservedName + Syntax::rUSELINK,
            Param::indicatedReservedName + Syntax::rPOSTLINK,
            Param::dso,
            Param::name,
            Param::indicatedReservedName + Syntax::rIMPLIED);

      if (!parseParam(isExplicit
                  ? allow2e
                  : (idlink ? allow2id : allow2i), declInputLevel, parm))
      return 0;
      if (parm.type == Param::indicatedReservedName + Syntax::rUSELINK) {
      static AllowedParams
        allowLinkSetEmpty(Param::name,
                      Param::indicatedReservedName + Syntax::rINITIAL,
                      Param::indicatedReservedName + Syntax::rEMPTY);
      if (!parseParam(allowLinkSetEmpty, declInputLevel, parm))
        return 0;
      const LinkSet *uselink;
      if (parm.type == Param::name)
        uselink = lookupCreateLinkSet(parm.token);
      else if (parm.type == Param::indicatedReservedName + Syntax::rINITIAL)
        uselink = defComplexLpd().initialLinkSet();
      else
        uselink = defComplexLpd().emptyLinkSet();
      linkRule->setUselink(uselink);
      static AllowedParams
        allow3i(Param::indicatedReservedName + Syntax::rPOSTLINK,
              Param::dso,
              Param::mdc,
              Param::name,
              Param::nameGroup);
      static AllowedParams
        allow3id(Param::indicatedReservedName + Syntax::rPOSTLINK,
               Param::dso,
               Param::mdc,
               Param::name);
      static AllowedParams
        allow3e(Param::indicatedReservedName + Syntax::rPOSTLINK,
              Param::dso,
              Param::name,
              Param::indicatedReservedName + Syntax::rIMPLIED);
      
      if (!parseParam(isExplicit
                  ? allow3e
                  : (idlink ? allow3id : allow3i),
                  declInputLevel, parm))
        return 0;
      }
      if (parm.type == Param::indicatedReservedName + Syntax::rPOSTLINK) {
      if (!parseParam(allowLinkSetSpec, declInputLevel, parm))
        return 0;
      const LinkSet *postlink;
      if (parm.type == Param::indicatedReservedName + Syntax::rRESTORE)
        linkRule->setPostlinkRestore();
      else {
        if (parm.type == Param::name)
          postlink = lookupCreateLinkSet(parm.token);
        else if (parm.type
               == Param::indicatedReservedName + Syntax::rINITIAL)
          postlink = defComplexLpd().initialLinkSet();
        else
          postlink = defComplexLpd().emptyLinkSet();
        linkRule->setPostlink(postlink);
      }
      static AllowedParams
        allow4i(Param::dso,
              Param::mdc,
              Param::name,
              Param::nameGroup);
      static AllowedParams
        allow4id(Param::dso,
               Param::mdc,
               Param::name);
      static AllowedParams
        allow4e(Param::dso,
              Param::name,
              Param::indicatedReservedName + Syntax::rIMPLIED);
      if (!parseParam(isExplicit
                  ? allow4e
                  : (idlink ? allow4id : allow4i),
                  declInputLevel, parm))
        return 0;
      }
      AttributeList attributes;
      ConstPtr<AttributeDefinitionList> attDef;
      for (size_t i = 0; i < assocElementTypes.size(); i++) {
      const ElementType *e = assocElementTypes[i];
      if (e) {
        if (i == 0)
          attDef = defComplexLpd().attributeDef(e);
        else if (attDef != defComplexLpd().attributeDef(e))
          message(ParserMessages::assocElementDifferentAtts);
        // FIXME recover from this
      }
      }
      attributes.init(attDef);
      
      if (parm.type == Param::dso) {
      Boolean netEnabling;
      Ptr<AttributeDefinitionList> newAttDef;
      if (!parseAttributeSpec(1, attributes, netEnabling, newAttDef))
        return 0;
      if (!newAttDef.isNull()) {
        newAttDef->setIndex(defComplexLpd().allocAttributeDefinitionListIndex());
        for (size_t i = 0; i < assocElementTypes.size(); i++) {
          const ElementType *e = assocElementTypes[i];
          if (e && defComplexLpd().attributeDef(e) == attDef)
            defComplexLpd().setAttributeDef(e, newAttDef);
        }
      }
      static AllowedParams
        allow5e(Param::name,
              Param::indicatedReservedName + Syntax::rIMPLIED);
      if (!parseParam(isExplicit
                  ? allow5e
                  : (idlink ? allowNameMdc : allowNameNameGroupMdc),
                  declInputLevel, parm))
        return 0;
      }
      else
      attributes.finish(*this);
      linkRule->setLinkAttributes(attributes);
      if (isExplicit) {
      Boolean resultImplied;
      const ElementType *resultType;
      AttributeList resultAttributes;
      if (!parseResultElementSpec(declInputLevel,
                            parm,
                            idlink,
                            resultImplied,
                            resultType,
                            resultAttributes))
        return 0;
      if (!resultImplied)
        linkRule->setResult(resultType, resultAttributes);
      }
      // Install the link rule.
      if (idlink) {
      idLinkRule.setAssocElementTypes(assocElementTypes);
      addIdLinkRule(id, idLinkRule);
      }
      else {
      if (!linkSet->defined()) {
        for (size_t i = 0; i < assocElementTypes.size(); i++)
          if (assocElementTypes[i])
            addLinkRule(linkSet, assocElementTypes[i], linkRuleResource);
      }
      }
    }
  } while (parm.type != Param::mdc);
  if (linkSet)
    linkSet->setDefined();
  if (currentMarkup()) {
    if (idlink)
      eventHandler().idLinkDecl(new (eventAllocator())
                        IdLinkDeclEvent(defComplexLpdPointer(),
                                    markupLocation(),
                                    currentMarkup()));
    else
      eventHandler().linkDecl(new (eventAllocator())
                        LinkDeclEvent(linkSet,
                                  defComplexLpdPointer(),
                                  markupLocation(),
                                  currentMarkup()));
  }
  return 1;
}

void Parser::addIdLinkRule(const StringC &id,
                     IdLinkRule &rule)
{
  IdLinkRuleGroup *group = defComplexLpd().lookupCreateIdLink(id);
  size_t nRules = group->nLinkRules();
  if ((nRules == 1 && group->linkRule(0).attributes().nSpec() == 0)
      || nRules >= 1 && rule.attributes().nSpec() == 0)
    message(ParserMessages::multipleIdLinkRuleAttribute,
          StringMessageArg(id));
  group->addLinkRule(rule);
}

void Parser::addLinkRule(LinkSet *linkSet,
                   const ElementType *sourceElement,
                   const ConstPtr<SourceLinkRuleResource> &linkRule)
{
  size_t nRules = linkSet->nLinkRules(sourceElement);
  if ((nRules == 1
       && linkSet->linkRule(sourceElement, 0).attributes().nSpec() == 0)
      || nRules >= 1 && linkRule->attributes().nSpec() == 0)
    message(ParserMessages::multipleLinkRuleAttribute,
          StringMessageArg(sourceElement->name()));
  linkSet->addLinkRule(sourceElement, linkRule);
}

class ResultAttributeSpecModeSetter {
public:
  ResultAttributeSpecModeSetter(ParserState *state) : state_(state) {
    state_->setResultAttributeSpecMode();
  }
  ~ResultAttributeSpecModeSetter() { clear(); }
  void clear() {
    if (state_) {
      state_->clearResultAttributeSpecMode();
      state_ = 0;
    }
  }
private:
  ParserState *state_;
};

Boolean Parser::parseResultElementSpec(unsigned declInputLevel,
                               Param &parm,
                               Boolean idlink,
                               Boolean &implied,
                               const ElementType *&resultType,
                               AttributeList &attributes)
{
  if (parm.type == Param::indicatedReservedName + Syntax::rIMPLIED) {
    if (!parseParam(idlink ? allowNameMdc : allowExplicitLinkRuleMdc,
                declInputLevel, parm))
      return 0;
    implied = 1;
  }
  else {
    implied = 0;
    ElementType *e = lookupResultElementType(parm.token);
    resultType = e;
    static AllowedParams
      allow(Param::dso,
          Param::mdc,
          Param::name,
          Param::nameGroup,
          Param::indicatedReservedName + Syntax::rIMPLIED);
    static AllowedParams
      allowNameDsoMdc(Param::dso,
                  Param::mdc,
                  Param::name);
    if (!parseParam(idlink ? allowNameDsoMdc : allow,
                declInputLevel, parm))
      return 0;
    ConstPtr<AttributeDefinitionList> attDef;
    if (e)
      attDef = e->attributeDef();
    attributes.init(attDef);
    if (parm.type == Param::dso) {
      ResultAttributeSpecModeSetter modeSetter(this);
      Boolean netEnabling;
      Ptr<AttributeDefinitionList> newAttDef;
      if (!parseAttributeSpec(1, attributes, netEnabling, newAttDef))
      return 0;
      if (!newAttDef.isNull()) {
      Ptr<Dtd> r(defComplexLpd().resultDtd());
      if (!r.isNull()) {
        newAttDef->setIndex(r->allocAttributeDefinitionListIndex());
        if (e)
          e->setAttributeDef(newAttDef);
      }
      }
      modeSetter.clear();
      if (attributes.nSpec() == 0)
      message(ParserMessages::emptyResultAttributeSpec);
      if (!parseParam(idlink ? allowNameMdc : allowExplicitLinkRuleMdc,
                  declInputLevel, parm))
      return 0;
    }
    else {
      // For entity and notation attributes.
      ResultAttributeSpecModeSetter modeSetter(this);
      attributes.finish(*this);
      modeSetter.clear();
    }
  }
  return 1;
}

ElementType *Parser::lookupResultElementType(const StringC &name)
{
  Dtd *dtd = defComplexLpd().resultDtd().pointer();
  if (!dtd)
    return 0;
  ElementType *e = dtd->lookupElementType(name);
  if (!e)
    message(ParserMessages::noSuchResultElement, StringMessageArg(name));
  return e;
}

Boolean Parser::parseUselinkDecl()
{
  unsigned declInputLevel = inputLevel();
  Param parm;
  if (!parseParam(allowLinkSetSpec, declInputLevel, parm))
    return 0;
  Param parm2;
  if (!parseParam(allowName, declInputLevel, parm2))
    return 0;
  StringC linkType;
  parm2.token.swap(linkType);
  if (!parseParam(allowMdc, declInputLevel, parm2))
    return 0;
  ConstPtr<Lpd> lpd = lookupLpd(linkType);
  if (lpd.isNull()) 
    message(ParserMessages::uselinkBadLinkType, StringMessageArg(linkType));
  else if (lpd->type() == Lpd::simpleLink)
    message(ParserMessages::uselinkSimpleLpd, StringMessageArg(linkType));
  else {
    const ComplexLpd *complexLpd = (const ComplexLpd *)lpd.pointer();
    const LinkSet *linkSet;
    Boolean restore = 0;
    if (parm.type == Param::name) {
      linkSet = complexLpd->lookupLinkSet(parm.token);
      if (!linkSet) {
      message(ParserMessages::uselinkBadLinkSet,
            StringMessageArg(complexLpd->name()),
            StringMessageArg(parm.token));
      return 1;
      }
    }
    else if (parm.type == Param::indicatedReservedName + Syntax::rINITIAL)
      linkSet = complexLpd->initialLinkSet();
    else if (parm.type == Param::indicatedReservedName + Syntax::rEMPTY)
      linkSet = complexLpd->emptyLinkSet();
    else {
      linkSet = 0;
      restore = 1;
    }
    if (lpd->active())
      eventHandler().uselink(new (eventAllocator())
                       UselinkEvent(lpd, linkSet,
                                restore, markupLocation(),
                                currentMarkup()));
    else
      eventHandler().ignoredMarkup(new (eventAllocator())
                           IgnoredMarkupEvent(markupLocation(),
                                          currentMarkup()));
  }
  return 1;
}

LinkSet *Parser::lookupCreateLinkSet(const StringC &name)
{
  LinkSet *linkSet = defComplexLpd().lookupLinkSet(name);
  if (!linkSet) {
    linkSet = new LinkSet(name, defComplexLpd().sourceDtd().pointer());
    defComplexLpd().insertLinkSet(linkSet);
  }
  return linkSet;
}

Boolean Parser::parseMarkedSectionDeclStart()
{
  if (markedSectionLevel() == syntax().taglvl())
    message(ParserMessages::markedSectionLevel,
          NumberMessageArg(syntax().taglvl()));
  if (!inInstance()
      && options().warnInternalSubsetMarkedSection
      && inputLevel() == 1)
    message(ParserMessages::internalSubsetMarkedSection);
  if (markedSectionSpecialLevel() > 0) {
    startMarkedSection(markupLocation());
    if (inInstance()
      ? eventsWanted().wantMarkedSections()
      : eventsWanted().wantPrologMarkup())
      eventHandler().ignoredChars(new (eventAllocator())
                          IgnoredCharsEvent(currentInput()->currentTokenStart(),
                                        currentInput()->currentTokenLength(),
                                        currentLocation(),
                                        0));
                          
    return 1;
  }
  Boolean discardMarkup;
  if (startMarkup(inInstance()
              ? eventsWanted().wantMarkedSections()
              : eventsWanted().wantPrologMarkup(),
              currentLocation())) {
    currentMarkup()->addDelim(Syntax::dMDO);
    currentMarkup()->addDelim(Syntax::dDSO);
    discardMarkup = 0;
  }
  else if (options().warnInstanceStatusKeywordSpecS && inInstance()) {
    startMarkup(1, currentLocation());
    discardMarkup = 1;
  }
  unsigned declInputLevel = inputLevel();
  static AllowedParams allowStatusDso(Param::dso,
                              Param::reservedName + Syntax::rCDATA,
                              Param::reservedName + Syntax::rRCDATA,
                              Param::reservedName + Syntax::rIGNORE,
                              Param::reservedName + Syntax::rINCLUDE,
                              Param::reservedName + Syntax::rTEMP);
  Param parm;
  MarkedSectionEvent::Status status = MarkedSectionEvent::include;
  if (!parseParam(allowStatusDso, declInputLevel, parm))
    return 0;
  if (options().warnMissingStatusKeyword && parm.type == Param::dso)
    message(ParserMessages::missingStatusKeyword);
  while (parm.type != Param::dso) {
    switch (parm.type) {
    case Param::reservedName + Syntax::rCDATA:
      if (status < MarkedSectionEvent::cdata)
      status = MarkedSectionEvent::cdata;
      break;
    case Param::reservedName + Syntax::rRCDATA:
      if (status < MarkedSectionEvent::rcdata)
      status = MarkedSectionEvent::rcdata;
      if (options().warnRcdataMarkedSection)
      message(ParserMessages::rcdataMarkedSection);
      break;
    case Param::reservedName + Syntax::rIGNORE:
      if (status < MarkedSectionEvent::ignore)
      status = MarkedSectionEvent::ignore;
      if (inInstance() && options().warnInstanceIgnoreMarkedSection)
      message(ParserMessages::instanceIgnoreMarkedSection);
      break;
    case Param::reservedName + Syntax::rINCLUDE:
      if (inInstance() && options().warnInstanceIncludeMarkedSection)
      message(ParserMessages::instanceIncludeMarkedSection);
      break;
    case Param::reservedName + Syntax::rTEMP:
      if (options().warnTempMarkedSection)
      message(ParserMessages::tempMarkedSection);
      break;
    }
    if (!parseParam(allowStatusDso, declInputLevel, parm))
      return 0;
    if (options().warnMultipleStatusKeyword
        && parm.type != Param::dso)
      message(ParserMessages::multipleStatusKeyword);
  }
  // FIXME this disallows
  // <!entity % e "include [ stuff ">
  // ...
  // <![ %e; ]]>
  // which I think is legal.

  if (inputLevel() > declInputLevel)
    message(ParserMessages::parameterEntityNotEnded);
  switch (status) {
  case MarkedSectionEvent::include:
    startMarkedSection(markupLocation());
    break;
  case MarkedSectionEvent::cdata:
    startSpecialMarkedSection(cmsMode, markupLocation());
    break;
  case MarkedSectionEvent::rcdata:
    startSpecialMarkedSection(rcmsMode, markupLocation());
    break;
  case MarkedSectionEvent::ignore:
    startSpecialMarkedSection(imsMode, markupLocation());
    break;
  }
  if (currentMarkup()) {
    if (options().warnInstanceStatusKeywordSpecS && inInstance()) {
      Location loc(markupLocation());
      for (MarkupIter iter(*currentMarkup()); iter.valid(); iter.advance(loc, syntaxPointer())) {
      if (iter.type() == Markup::s) {
        setNextLocation(loc);
        message(ParserMessages::instanceStatusKeywordSpecS);
      }
      }
      if (discardMarkup)
      startMarkup(0, markupLocation());
    }
    eventHandler().markedSectionStart(new (eventAllocator())
                              MarkedSectionStartEvent(status,
                                                markupLocation(),
                                                currentMarkup()));
  }
  return 1;
}

void Parser::handleMarkedSectionEnd()
{
  if (markedSectionLevel() == 0)
    message(ParserMessages::markedSectionEnd);
  else {
    if (inInstance()
      ? eventsWanted().wantMarkedSections()
      : eventsWanted().wantPrologMarkup()) {
      if (markedSectionSpecialLevel() > 1)
      eventHandler().ignoredChars(new (eventAllocator())
                            IgnoredCharsEvent(currentInput()->currentTokenStart(),
                                          currentInput()->currentTokenLength(),
                                          currentLocation(),
                                          0));
      else {
      MarkedSectionEvent::Status status;
      switch (currentMode()) {
      case cmsMode:
        status = MarkedSectionEvent::cdata;
        break;
      case rcmsMode:
        status = MarkedSectionEvent::rcdata;
        break;
      case imsMode:
        status = MarkedSectionEvent::ignore;
        break;
      default:
        status = MarkedSectionEvent::include;
        break;
      }
      startMarkup(1, currentLocation());
      currentMarkup()->addDelim(Syntax::dMSC);
      currentMarkup()->addDelim(Syntax::dMDC);
      eventHandler().markedSectionEnd(new (eventAllocator())
                              MarkedSectionEndEvent(status,
                                                markupLocation(),
                                                currentMarkup()));
      }
    }
    endMarkedSection();
  }
}

void Parser::emptyCommentDecl()
{
  if (startMarkup(eventsWanted().wantCommentDecls(), currentLocation())) {
    currentMarkup()->addDelim(Syntax::dMDO);
    currentMarkup()->addDelim(Syntax::dMDC);
    eventHandler().commentDecl(new (eventAllocator())
                         CommentDeclEvent(markupLocation(),
                                    currentMarkup()));
  }
  if (options().warnEmptyCommentDecl)
    message(ParserMessages::emptyCommentDecl);
}

Boolean Parser::parseCommentDecl()
{
  if (startMarkup(inInstance()
              ? eventsWanted().wantCommentDecls()
              : eventsWanted().wantPrologMarkup(),
              currentLocation()))
    currentMarkup()->addDelim(Syntax::dMDO);
  if (!parseComment(comMode))
    return 0;
  for (;;) {
    Token token = getToken(mdMode);
    switch (token) {
    case tokenS:
      if (currentMarkup())
      currentMarkup()->addS(currentChar());
      if (options().warnCommentDeclS)
      message(ParserMessages::commentDeclS);
      break;
    case tokenCom:
      if (!parseComment(comMode))
      return 0;
      if (options().warnCommentDeclMultiple)
      message(ParserMessages::commentDeclMultiple);
      break;
    case tokenMdc:
      if (currentMarkup())
      currentMarkup()->addDelim(Syntax::dMDC);
      goto done;
    case tokenEe:
      message(ParserMessages::declarationLevel);
      return 0;
    case tokenUnrecognized:
      if (reportNonSgmlCharacter())
      break;
      // braces to work round Sun C++ 4.0 bug
      {
      message(ParserMessages::commentDeclarationCharacter,
            StringMessageArg(currentToken()),
            markupLocation());
      }
      return 0;
    default:
      // braces to work round Sun C++ 4.0 bug
      {
      message(ParserMessages::commentDeclInvalidToken,
            TokenMessageArg(token, mdMode, syntaxPointer(), sdPointer()),
            markupLocation());
      }
      return 0;
    }
  }
 done:
  if (currentMarkup())
    eventHandler().commentDecl(new (eventAllocator())
                         CommentDeclEvent(markupLocation(),
                                    currentMarkup()));
  return 1;
}

Boolean Parser::parseAfdrDecl()
{
  unsigned declInputLevel = inputLevel();
  static AllowedParams allowMinimumLiteral(Param::minimumLiteral);
  Param parm;
  setHadAfdrDecl();
  if (!parseParam(allowMinimumLiteral, declInputLevel, parm))
    return 0;
  if (parm.literalText.string() != sd().execToInternal("ISO/IEC 10744:1997"))
    message(ParserMessages::afdrVersion,
          StringMessageArg(parm.literalText.string()));
  if (!parseParam(allowMdc, declInputLevel, parm))
    return 0;
  eventHandler().ignoredMarkup(new (eventAllocator())
                         IgnoredMarkupEvent(markupLocation(),
                                      currentMarkup()));
  return 1;
}

#ifdef SP_NAMESPACE
}
#endif

Generated by  Doxygen 1.6.0   Back to index