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

#include "splib.h"
#include "Parser.h"
#include "macros.h"
#include "SdFormalError.h"
#include "MessageBuilder.h"
#include "ParserMessages.h"
#include "MessageArg.h"
#include "CharsetRegistry.h"
#include "ISetIter.h"
#include "token.h"
#include "TokenMessageArg.h"
#include "constant.h"
#include "SdText.h"
#include "NumericCharRefOrigin.h"

#ifdef SP_NAMESPACE
namespace SP_NAMESPACE {
#endif

class CharSwitcher {
public:
  CharSwitcher();
  void addSwitch(WideChar from, WideChar to);
  SyntaxChar subst(WideChar c);
  size_t nSwitches() const;
  Boolean switchUsed(size_t i) const;
  WideChar switchFrom(size_t i) const;
  WideChar switchTo(size_t i) const;
private:
  Vector<PackedBoolean> switchUsed_;
  Vector<WideChar> switches_;
};

// Information about the SGML declaration being built.

struct SdBuilder {
  SdBuilder();
  void addFormalError(const Location &, const MessageType1 &, const StringC &);
  Ptr<Sd> sd;
  Ptr<Syntax> syntax;
  CharsetDecl syntaxCharsetDecl;
  CharsetInfo syntaxCharset;
  CharSwitcher switcher;
  Boolean externalSyntax;
  Boolean enr;
  Boolean www;
  Boolean valid;
  Boolean external;
  IList<SdFormalError> formalErrorList;
};

class CharsetMessageArg : public MessageArg {
public:
  CharsetMessageArg(const ISet<WideChar> &set);
  MessageArg *copy() const;
  void append(MessageBuilder &) const;
private:
  ISet<WideChar> set_;
};

struct SdParam {
  typedef unsigned char Type;
  enum {
    invalid,
    eE,
    minimumLiteral,
    mdc,
    minus,
    number,
    capacityName,
    name,
    paramLiteral,
    systemIdentifier,
    generalDelimiterName,
    referenceReservedName,
    quantityName,
    reservedName		// Sd::ReservedName is added to this
  };
  Type type;
  StringC token;
  Text literalText;
  String<SyntaxChar> paramLiteralText;
  union {
    Number n;
    Sd::Capacity capacityIndex;
    Syntax::Quantity quantityIndex;
    Syntax::ReservedName reservedNameIndex;
    Syntax::DelimGeneral delimGeneralIndex;
  };
};

class AllowedSdParams {
public:
  AllowedSdParams(SdParam::Type,
		  SdParam::Type = SdParam::invalid,
		  SdParam::Type = SdParam::invalid,
		  SdParam::Type = SdParam::invalid,
		  SdParam::Type = SdParam::invalid,
		  SdParam::Type = SdParam::invalid);
  Boolean param(SdParam::Type) const;
  SdParam::Type get(int i) const;
private:
  enum { maxAllow = 6 };
  SdParam::Type allow_[maxAllow];
};

class AllowedSdParamsMessageArg : public MessageArg {
public:
  AllowedSdParamsMessageArg(const AllowedSdParams &allow,
			    const ConstPtr<Sd> &sd);
  MessageArg *copy() const;
  void append(MessageBuilder &) const;
private:
  AllowedSdParams allow_;
  ConstPtr<Sd> sd_;
};

struct StandardSyntaxSpec {
  struct AddedFunction {
    const char *name;
    Syntax::FunctionClass functionClass;
    SyntaxChar syntaxChar;
  };
  const AddedFunction *addedFunction;
  size_t nAddedFunction;
  Boolean shortref;
};

static StandardSyntaxSpec::AddedFunction coreFunctions[] = {
  { "TAB", Syntax::cSEPCHAR, 9 },
};

static StandardSyntaxSpec coreSyntax = {
  coreFunctions, SIZEOF(coreFunctions), 0
};

static StandardSyntaxSpec refSyntax = {
  coreFunctions, SIZEOF(coreFunctions), 1
};

void Parser::doInit()
{
  if (cancelled()) {
    allDone();
    return;
  }
  // When document entity doesn't exist, don't give any errors
  // other than the cannot open error.
  if (currentInput()->get(messenger()) == InputSource::eE) {
    if (currentInput()->accessError()) {
      allDone();
      return;
    }
  }
  else
    currentInput()->ungetToken();
  const CharsetInfo &initCharset = sd().internalCharset();
  ISet<WideChar> missing;
  findMissingMinimum(initCharset, missing);
  if (!missing.isEmpty()) {
    message(ParserMessages::sdMissingCharacters, CharsetMessageArg(missing));
    giveUp();
    return;
  }
  Boolean found = 0;
  StringC systemId;
  if (scanForSgmlDecl(initCharset)) {
    if (options().warnExplicitSgmlDecl)
      message(ParserMessages::explicitSgmlDecl);
    found = 1;
  }
  else {
    currentInput()->ungetToken();
    if (entityCatalog().sgmlDecl(initCharset, messenger(), systemId)) {
      InputSource *in = entityManager().open(systemId,
					     sd().docCharset(),
					     InputSourceOrigin::make(),
					     0,
					     messenger());
      if (in) {
	pushInput(in);
	if (scanForSgmlDecl(initCharset))
	  found = 1;
	else {
	  message(ParserMessages::badDefaultSgmlDecl);
	  popInputStack();
	}
      }
    }
  }
  if (found) {
    if (startMarkup(eventsWanted().wantPrologMarkup(), currentLocation())) {
      size_t nS = currentInput()->currentTokenLength() - 6;
      for (size_t i = 0; i < nS; i++)
	currentMarkup()->addS(currentInput()->currentTokenStart()[i]);
      currentMarkup()->addDelim(Syntax::dMDO);
      currentMarkup()->addSdReservedName(Sd::rSGML,
					currentInput()->currentTokenStart()
					+ (currentInput()->currentTokenLength() - 4),
					4);
    }
    Syntax *syntaxp = new Syntax(sd());
    CharSwitcher switcher;
    if (!setStandardSyntax(*syntaxp, refSyntax, sd().internalCharset(), switcher, 0)) {
      giveUp();
      return;
    }
    syntaxp->implySgmlChar(sd());
    setSyntax(syntaxp);
    compileSdModes();
    ConstPtr<Sd> refSd(sdPointer());
    ConstPtr<Syntax> refSyntax(syntaxPointer());
    if (!parseSgmlDecl()) {
      giveUp();
      return;
    }
    // queue an SGML declaration event
    eventHandler().sgmlDecl(new (eventAllocator())
			    SgmlDeclEvent(sdPointer(),
					  syntaxPointer(),
					  instanceSyntaxPointer(),
					  refSd,
					  refSyntax,
					  currentInput()->nextIndex(),
					  systemId,
					  markupLocation(),
					  currentMarkup()));
    if (inputLevel() == 2) {
      // FIXME perhaps check for junk after SGML declaration
      popInputStack();
    }
  }
  else {
    if (!implySgmlDecl()) {
      giveUp();
      return;
    }
    currentInput()->willNotSetDocCharset();
    // queue an SGML declaration event
    eventHandler().sgmlDecl(new (eventAllocator())
			    SgmlDeclEvent(sdPointer(),
					  syntaxPointer()));
  }
						      
  // Now we have sd and syntax set up, prepare to parse the prolog.
  compilePrologModes();
  setPhase(prologPhase);
}

Boolean Parser::implySgmlDecl()
{
  Syntax *syntaxp = new Syntax(sd());
  const StandardSyntaxSpec *spec;
  if (options().shortref)
    spec = &refSyntax;
  else
    spec = &coreSyntax;
  CharSwitcher switcher;
  if (!setStandardSyntax(*syntaxp, *spec, sd().internalCharset(), switcher, 0))
    return 0;
  syntaxp->implySgmlChar(sd());
  for (int i = 0; i < Syntax::nQuantity; i++)
    syntaxp->setQuantity(i, options().quantity[i]);
  setSyntax(syntaxp);
  return 1;
}

Boolean Parser::setStandardSyntax(Syntax &syn,
				  const StandardSyntaxSpec &spec,
				  const CharsetInfo &internalCharset,
				  CharSwitcher &switcher,
				  Boolean www)
{
  static UnivCharsetDesc::Range syntaxCharsetRanges[] = {
    { 0, 128, 0 },
  };
  static UnivCharsetDesc syntaxCharsetDesc(syntaxCharsetRanges,
					   SIZEOF(syntaxCharsetRanges));
  static CharsetInfo syntaxCharset(syntaxCharsetDesc);

  Boolean valid = 1;
  if (!checkSwitches(switcher, syntaxCharset))
    valid = 0;
  size_t i;
  for (i = 0; i < switcher.nSwitches(); i++)
    if (switcher.switchTo(i) >= 128)
      message(ParserMessages::switchNotInCharset,
	      NumberMessageArg(switcher.switchTo(i)));
  static const Char shunchar[] = {
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
    127, 255
    };
  
  for (i = 0; i < SIZEOF(shunchar); i++)
    syn.addShunchar(shunchar[i]);
  syn.setShuncharControls();
  static Syntax::StandardFunction standardFunctions[3] = {
    Syntax::fRE, Syntax::fRS, Syntax::fSPACE
  };
  static SyntaxChar functionChars[3] = { 13, 10, 32 };
  for (i = 0; i < 3; i++) {
    Char docChar;
    if (translateSyntax(switcher,
			syntaxCharset,
			internalCharset,
			functionChars[i],
			docChar)
	&& checkNotFunction(syn, docChar))
      syn.setStandardFunction(standardFunctions[i], docChar);
    else
      valid = 0;
  }
  for (i = 0; i < spec.nAddedFunction; i++) {
    Char docChar;
    if (translateSyntax(switcher,
			syntaxCharset,
			internalCharset,
			spec.addedFunction[i].syntaxChar,
			docChar)
	&& checkNotFunction(syn, docChar))
      syn.addFunctionChar(internalCharset.execToDesc(spec.addedFunction[i].name),
			  spec.addedFunction[i].functionClass,
			  docChar);
    else
      valid = 0;
  }
  
  static SyntaxChar nameChars[2] = { 45, 46 }; // '-' '.'
  ISet<Char> nameCharSet;
  for (i = 0; i < 2; i++) {
    Char docChar;
    if (translateSyntax(switcher,
			syntaxCharset,
			internalCharset,
			nameChars[i],
			docChar))
	nameCharSet.add(docChar);
    else
      valid = 0;
  }
  if (!checkNmchars(nameCharSet, syn))
    valid = 0;
  else
    syn.addNameCharacters(nameCharSet);
  syn.setNamecaseGeneral(1);
  syn.setNamecaseEntity(0);
  if (!setRefDelimGeneral(syn, syntaxCharset, internalCharset, switcher))
    valid = 0;
  setRefNames(syn, internalCharset, www);
  syn.enterStandardFunctionNames();
  if (spec.shortref
      && !addRefDelimShortref(syn, syntaxCharset, internalCharset, switcher))
    valid = 0;
  return valid;
}

Boolean Parser::setRefDelimGeneral(Syntax &syntax,
				   const CharsetInfo &syntaxCharset,
				   const CharsetInfo &internalCharset,
				   CharSwitcher &switcher)
{
  // Column 3 from Figure 3
  static const char delims[][2] = {
    { 38 },
    { 45, 45 },
    { 38, 35 },
    { 93 },
    { 91 },
    { 93 },
    { 91 },
    { 38 },
    { 60, 47 },
    { 41 },
    { 40 },
    { 0 }, // HCRO
    { 34 },
    { 39 },
    { 62 },
    { 60, 33 },
    { 45 },
    { 93, 93 },
    { 47 },
    { 47 }, // NESTC
    { 63 },
    { 124 },
    { 37 },
    { 62 },
    { 60, 63 },
    { 43 },
    { 59 },
    { 42 },
    { 35 },
    { 44 },
    { 60 },
    { 62 },
    { 61 },
  };
  Boolean valid = 1;
  ISet<WideChar> missing;
  for (int i = 0; i < Syntax::nDelimGeneral; i++)
    if (syntax.delimGeneral(i).size() == 0) {
      StringC delim;
      size_t j;
      for (j = 0; j < 2 && delims[i][j] != '\0'; j++) {
	UnivChar univChar = translateUniv(delims[i][j], switcher,
					  syntaxCharset);
	Char c;
	if (univToDescCheck(internalCharset, univChar, c))
	  delim += c;
	else {
	  missing += univChar;
	  valid = 0;
	}
      }
      if (delim.size() == j) {
	if (checkGeneralDelim(syntax, delim))
	  syntax.setDelimGeneral(i, delim);
	else
	  valid = 0;
      }
    }
  if (!missing.isEmpty())
    message(ParserMessages::missingSignificant646, CharsetMessageArg(missing));
  return valid;
}

void Parser::setRefNames(Syntax &syntax, const CharsetInfo &internalCharset,
			 Boolean www)
{
  static const char *const referenceNames[] = {
    "ALL",
    "ANY",
    "ATTLIST",
    "CDATA",
    "CONREF",
    "CURRENT",
    "DATA",
    "DEFAULT",
    "DOCTYPE",
    "ELEMENT",
    "EMPTY",
    "ENDTAG",
    "ENTITIES",
    "ENTITY",
    "FIXED",
    "ID",
    "IDLINK",
    "IDREF",
    "IDREFS",
    "IGNORE",
    "IMPLICIT",
    "IMPLIED",
    "INCLUDE",
    "INITIAL",
    "LINK",
    "LINKTYPE",
    "MD",
    "MS",
    "NAME",
    "NAMES",
    "NDATA",
    "NMTOKEN",
    "NMTOKENS",
    "NOTATION",
    "NUMBER",
    "NUMBERS",
    "NUTOKEN",
    "NUTOKENS",
    "O",
    "PCDATA",
    "PI",
    "POSTLINK",
    "PUBLIC",
    "RCDATA",
    "RE",
    "REQUIRED",
    "RESTORE",
    "RS",
    "SDATA",
    "SHORTREF",
    "SIMPLE",
    "SPACE",
    "STARTTAG",
    "SUBDOC",
    "SYSTEM",
    "TEMP",
    "USELINK",
    "USEMAP"
    };
  for (int i = 0; i < Syntax::nNames; i++) {
    switch (i) {
    case Syntax::rDATA:
    case Syntax::rIMPLICIT:
      if (!www)
	break;
      // fall through
    case Syntax::rALL:
      if (!www && options().errorAfdr)
	break;
      // fall through
    default:
      {
	StringC docName(internalCharset.execToDesc(referenceNames[i]));
	Syntax::ReservedName tem;
        if (syntax.lookupReservedName(docName, &tem))
	  message(ParserMessages::nameReferenceReservedName,
	          StringMessageArg(docName));
        if (syntax.reservedName(Syntax::ReservedName(i)).size() == 0)
	  syntax.setName(i, docName);
        break;
      }
    }
  }
}

Boolean Parser::addRefDelimShortref(Syntax &syntax,
				    const CharsetInfo &syntaxCharset,
				    const CharsetInfo &internalCharset,
				    CharSwitcher &switcher)
{
  // Column 2 from Figure 4
  static const char delimShortref[][3] = {
    { 9 },
    { 13 },
    { 10 },
    { 10, 66 },
    { 10, 13 },
    { 10, 66, 13 },
    { 66, 13 },
    { 32 },
    { 66, 66 },
    { 34 },
    { 35 },
    { 37 },
    { 39 },
    { 40 },
    { 41 },
    { 42 },
    { 43 },
    { 44 },
    { 45 },
    { 45, 45 },
    { 58 },
    { 59 },
    { 61 },
    { 64 },
    { 91 },
    { 93 },
    { 94 },
    { 95 },
    { 123 },
    { 124 },
    { 125 },
    { 126 },
  };
  ISet<WideChar> missing;

  for (size_t i = 0; i < SIZEOF(delimShortref); i++) {
    StringC delim;
    
    size_t j;
    for (j = 0; j < 3 && delimShortref[i][j] != '\0'; j++) {
      Char c;
      UnivChar univChar = translateUniv(delimShortref[i][j], switcher,
					syntaxCharset);
      if (univToDescCheck(internalCharset, univChar, c))
	delim += c;
      else
	missing += univChar;
    }
    if (delim.size() == j) {
      if (switcher.nSwitches() > 0 && syntax.isValidShortref(delim))
	message(ParserMessages::duplicateDelimShortref,
		StringMessageArg(delim));
      else
	syntax.addDelimShortref(delim, internalCharset);
    }
  }
  if (!missing.isEmpty())
    message(ParserMessages::missingSignificant646, CharsetMessageArg(missing));
  return 1;
}

// Determine whether the document starts with an SGML declaration.
// There is no current syntax at this point.

Boolean Parser::scanForSgmlDecl(const CharsetInfo &initCharset)
{
  Char rs;
  if (!univToDescCheck(initCharset, UnivCharsetDesc::rs, rs))
    return 0;
  Char re;
  if (!univToDescCheck(initCharset, UnivCharsetDesc::re, re))
    return 0;
  Char space;
  if (!univToDescCheck(initCharset, UnivCharsetDesc::space, space))
    return 0;
  Char tab;
  if (!univToDescCheck(initCharset, UnivCharsetDesc::tab, tab))
    return 0;
  InputSource *in = currentInput();
  Xchar c = in->get(messenger());
  while (c == rs || c == space || c == re || c == tab)
    c = in->tokenChar(messenger());
  if (c != initCharset.execToDesc('<'))
    return 0;
  if (in->tokenChar(messenger()) != initCharset.execToDesc('!'))
    return 0;
  c = in->tokenChar(messenger());
  if (c != initCharset.execToDesc('S')
      && c != initCharset.execToDesc('s'))
    return 0;
  c = in->tokenChar(messenger());
  if (c != initCharset.execToDesc('G')
      && c != initCharset.execToDesc('g'))
    return 0;
  c = in->tokenChar(messenger());
  if (c != initCharset.execToDesc('M')
      && c != initCharset.execToDesc('m'))
    return 0;
  c = in->tokenChar(messenger());
  if (c != initCharset.execToDesc('L')
      && c != initCharset.execToDesc('l'))
    return 0;
  c = in->tokenChar(messenger());
  // Don't recognize this if SGML is followed by a name character.
  if (c == InputSource::eE)
    return 1;
  in->endToken(in->currentTokenLength() - 1);
  if (c == initCharset.execToDesc('-'))
    return 0;
  if (c == initCharset.execToDesc('.'))
    return 0;
  UnivChar univ;
  if (!initCharset.descToUniv(c, univ)) 
    return 1;
  if (UnivCharsetDesc::a <= univ && univ < UnivCharsetDesc::a + 26)
    return 0;
  if (UnivCharsetDesc::A <= univ && univ < UnivCharsetDesc::A + 26)
    return 0;
  if (UnivCharsetDesc::zero <= univ && univ < UnivCharsetDesc::zero + 10)
    return 0;
  return 1;
}
	    
void Parser::findMissingMinimum(const CharsetInfo &charset,
				ISet<WideChar> &missing)
{
  Char to;
  size_t i;
  for (i = 0; i < 26; i++) {
    if (!univToDescCheck(charset, UnivCharsetDesc::A + i, to))
      missing += UnivCharsetDesc::A + i;
    if (!univToDescCheck(charset, UnivCharsetDesc::a + i, to))
      missing += UnivCharsetDesc::a + i;
  }
  for (i = 0; i < 10; i++) {
    Char to;
    if (!univToDescCheck(charset, UnivCharsetDesc::zero + i, to))
      missing += UnivCharsetDesc::zero + i;
  }
  static const UnivChar special[] = {
    39, 40, 41, 43, 44, 45, 46, 47, 58, 61, 63
    };

  for (i = 0; i < SIZEOF(special); i++)
    if (!univToDescCheck(charset, special[i], to))
      missing += special[i];
}


Boolean Parser::parseSgmlDecl()
{
  SdParam parm;
  SdBuilder sdBuilder;

  if (!parseSdParam(AllowedSdParams(SdParam::minimumLiteral, SdParam::name), parm))
    return 0;
  if (parm.type == SdParam::name) {
    sdBuilder.external = 1;
    Location loc(currentLocation());
    StringC name;
    parm.token.swap(name);
    ExternalId externalId;
    if (!sdParseSgmlDeclRef(sdBuilder, parm, externalId))
      return 0;
    ExternalEntity *entity
      = new ExternalTextEntity(name, EntityDecl::sgml, loc, externalId);
    ConstPtr<Entity> entityPtr(entity);
    entity->generateSystemId(*this);
    if (entity->externalId().effectiveSystemId().size() == 0) {
      message(ParserMessages::cannotGenerateSystemIdSgml);
      return 0;
    }
    Ptr<EntityOrigin> origin(EntityOrigin::make(internalAllocator(), entityPtr, loc));
    if (currentMarkup())
      currentMarkup()->addEntityStart(origin);
    pushInput(entityManager().open(entity->externalId().effectiveSystemId(),
		                   sd().docCharset(),
				   origin.pointer(),
				   0,
				   messenger()));
    if (!parseSdParam(AllowedSdParams(SdParam::minimumLiteral), parm))
      return 0;
  }
  StringC version(sd().execToInternal("ISO 8879:1986"));
  StringC enrVersion(sd().execToInternal("ISO 8879:1986 (ENR)"));
  StringC wwwVersion(sd().execToInternal("ISO 8879:1986 (WWW)"));
  if (parm.literalText.string() == enrVersion)
    sdBuilder.enr = 1;
  else if (parm.literalText.string() == wwwVersion) {
    sdBuilder.enr = 1;
    sdBuilder.www = 1;
  }
  else if (parm.literalText.string() != version)
    message(ParserMessages::standardVersion,
	    StringMessageArg(parm.literalText.string()));
  if (sdBuilder.external && !sdBuilder.www)
    message(ParserMessages::sgmlDeclRefRequiresWww);
  sdBuilder.sd = new Sd(entityManagerPtr());
  if (sdBuilder.www)
    sdBuilder.sd->setWww(1);
  typedef Boolean (Parser::*SdParser)(SdBuilder &, SdParam &);
  static SdParser parsers[] = {
    &Parser::sdParseDocumentCharset,
    &Parser::sdParseCapacity,
    &Parser::sdParseScope,
    &Parser::sdParseSyntax,
    &Parser::sdParseFeatures,
    &Parser::sdParseAppinfo,
    &Parser::sdParseSeealso,
  };
  for (size_t i = 0; i < SIZEOF(parsers); i++) {
    if (!(this->*(parsers[i]))(sdBuilder, parm))
      return 0;
    if (!sdBuilder.valid)
      return 0;
  }
  setSdOverrides(*sdBuilder.sd);
  if (sdBuilder.sd->formal()) {
    while (!sdBuilder.formalErrorList.empty()) {
      SdFormalError *p = sdBuilder.formalErrorList.get();
      ParserState *state = this; // work around lcc 3.0 bug
      p->send(*state);
      delete p;
    }
  }
  setSd(sdBuilder.sd.pointer());
  currentInput()->setDocCharset(sd().docCharset(), entityManager().charset());
  if (sdBuilder.sd->scopeInstance()) {
    Syntax *proSyntax = new Syntax(sd());
    CharSwitcher switcher;
    setStandardSyntax(*proSyntax, refSyntax, sd().internalCharset(), switcher, sdBuilder.www);
    proSyntax->setSgmlChar(*sdBuilder.syntax->charSet(Syntax::sgmlChar));
    ISet<WideChar> invalidSgmlChar;
    proSyntax->checkSgmlChar(*sdBuilder.sd,
			     sdBuilder.syntax.pointer(),
			     1,  // get results in document character set
			     invalidSgmlChar);
    sdBuilder.syntax->checkSgmlChar(*sdBuilder.sd,
				    proSyntax,
				    1, // get results in document character set
				    invalidSgmlChar);
    if (!invalidSgmlChar.isEmpty())
      message(ParserMessages::invalidSgmlChar, CharsetMessageArg(invalidSgmlChar));
    setSyntaxes(proSyntax, sdBuilder.syntax.pointer());
  }
  else
    setSyntax(sdBuilder.syntax.pointer());
  if (syntax().multicode())
    currentInput()->setMarkupScanTable(syntax().markupScanTable());
  return 1;
}

Boolean Parser::sdParseSgmlDeclRef(SdBuilder &sdBuilder, SdParam &parm,
				   ExternalId &id)
{
  id.setLocation(currentLocation());
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rSYSTEM,
                                    SdParam::reservedName + Sd::rPUBLIC,
				    SdParam::mdc),
		    parm))
    return 0;
  if (parm.type == SdParam::mdc)
    return 1;
  if (parm.type == SdParam::reservedName + Sd::rPUBLIC) {
    if (!parseSdParam(AllowedSdParams(SdParam::minimumLiteral), parm))
      return 0;
    const MessageType1 *err;
    PublicId::TextClass textClass;
    if (!id.setPublic(parm.literalText, sd().internalCharset(), syntax().space(), err))
      sdBuilder.addFormalError(currentLocation(), *err, id.publicId()->string());
    else if (id.publicId()->getTextClass(textClass)
	     && textClass != PublicId::SD)
      sdBuilder.addFormalError(currentLocation(),
			       ParserMessages::sdTextClass,
			       id.publicId()->string());
  }
  if (!parseSdParam(AllowedSdParams(SdParam::systemIdentifier, SdParam::mdc), parm))
    return 0;
  if (parm.type == SdParam::mdc)
    return 1;
  id.setSystem(parm.literalText);
  return parseSdParam(AllowedSdParams(SdParam::mdc), parm);
}

Boolean Parser::sdParseDocumentCharset(SdBuilder &sdBuilder, SdParam &parm)
{
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rCHARSET),
		    parm))
    return 0;
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rBASESET),
		    parm))
    return 0;
  CharsetDecl decl;
  UnivCharsetDesc desc;
  if (!sdParseCharset(sdBuilder, parm, 1, decl, desc))
    return 0;
  ISet<WideChar> missing;
  findMissingMinimum(desc, missing);
  if (!missing.isEmpty()) {
    message(ParserMessages::missingMinimumChars,
	    CharsetMessageArg(missing));
    return 0;
  }
  ISet<Char> sgmlChar;
  decl.usedSet(sgmlChar);
  sdBuilder.sd->setDocCharsetDesc(desc);
  sdBuilder.sd->setDocCharsetDecl(decl);
  sdBuilder.syntax = new Syntax(*sdBuilder.sd);
  if (sd().internalCharsetIsDocCharset())
    sdBuilder.syntax->setSgmlChar(sgmlChar);
  else {
    ISet<Char> internalSgmlChar;
    translateDocSet(sdBuilder.sd->docCharset(), sdBuilder.sd->internalCharset(),
                    sgmlChar, internalSgmlChar);
    sdBuilder.syntax->setSgmlChar(internalSgmlChar);
  }
  return 1;
}

void Parser::translateDocSet(const CharsetInfo &fromCharset,
			     const CharsetInfo &toCharset,
			     const ISet<Char> &fromSet,
			     ISet<Char> &toSet)
{
  ISetIter<Char> iter(fromSet);
  Char min, max;
  while (iter.next(min, max)) {
    do {
      UnivChar univChar;
      Char internalChar;
      WideChar count2, alsoMax;
      if (!fromCharset.descToUniv(min, univChar, alsoMax)) {
	if (alsoMax >= max)
	  break;
	min = alsoMax;
      }
      else {
	  // FIXME better not to use univToDescCheck here
	  // Maybe OK if multiple internal chars corresponding to doc char
	int nMap = univToDescCheck(toCharset, univChar, internalChar, count2);
	if (alsoMax > max)
	  alsoMax = max;
	if (alsoMax - min > count2 - 1)
	  alsoMax = min + (count2 - 1);
	if (nMap)
	  toSet.addRange(internalChar, internalChar + (alsoMax - min));
	min = alsoMax;
      }
    } while (min++ != max);
  }
}

Boolean Parser::sdParseCharset(SdBuilder &sdBuilder,
			       SdParam &parm,
			       Boolean isDocument,
			       CharsetDecl &decl,
			       UnivCharsetDesc &desc)
{
  decl.clear();
  ISet<WideChar> multiplyDeclared;
  // This is for checking whether the syntax reference character set
  // is ISO 646 when SCOPE is INSTANCE.
  Boolean maybeISO646 = 1;
  do {
    if (!parseSdParam(AllowedSdParams(SdParam::minimumLiteral), parm))
      return 0;
    UnivCharsetDesc baseDesc;
    PublicId id;
    Boolean found;
    PublicId::TextClass textClass;
    const MessageType1 *err;
    if (!id.init(parm.literalText, sd().internalCharset(), syntax().space(), err))
      sdBuilder.addFormalError(currentLocation(),
			       *err,
			       id.string());
    else if (id.getTextClass(textClass)
	     && textClass != PublicId::CHARSET)
      sdBuilder.addFormalError(currentLocation(),
			       ParserMessages::basesetTextClass,
			       id.string());
    Boolean givenError;
    if (referencePublic(id, PublicId::CHARSET, givenError))
      found = sdParseExternalCharset(*sdBuilder.sd, baseDesc);
    else if (!givenError) {
      found = 0;
      PublicId::OwnerType ownerType;
      if (id.getOwnerType(ownerType) && ownerType == PublicId::ISO) {
	StringC sequence;
	if (id.getDesignatingSequence(sequence)) {
	  CharsetRegistry::ISORegistrationNumber number
	   = CharsetRegistry::getRegistrationNumber(sequence, sd().internalCharset());
	  if (number != CharsetRegistry::UNREGISTERED) {
	    Owner<CharsetRegistry::Iter> iter(CharsetRegistry::makeIter(number));
	    if (iter) {
	      found = 1;
	      WideChar min;
	      WideChar max;
	      UnivChar univ;
	      while (iter->next(min, max, univ))
		baseDesc.addRange(min, max, univ);
	    }
	  }
	}
      }
      if (!found)
	message(ParserMessages::unknownBaseset, StringMessageArg(id.string()));
    }
    else
      found = 0;
    if (!found)
      maybeISO646 = 0;
    decl.addSection(id);
    if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rDESCSET),
		      parm))
      return 0;
    if (!parseSdParam(AllowedSdParams(SdParam::number), parm))
      return 0;
    do {
      WideChar min = parm.n;
      if (!parseSdParam(AllowedSdParams(SdParam::number), parm))
	return 0;
      Number count = parm.n;
      Number adjCount;
      if (options().warnSgmlDecl && count == 0)
	message(ParserMessages::zeroNumberOfCharacters);
      decl.rangeDeclared(min, count, multiplyDeclared);
      if (isDocument
	  && count > 0
	  && (min > charMax || count - 1 > charMax - min)) {
	message(ParserMessages::documentCharMax, NumberMessageArg(charMax));
	adjCount = min > charMax ? 0 : 1 + (charMax - min);
	maybeISO646 = 0;
      }
      else
	adjCount = count;
      if (!parseSdParam(AllowedSdParams(SdParam::number,
					SdParam::minimumLiteral,
					SdParam::reservedName + Sd::rUNUSED),
			parm))
	return 0;
      switch (parm.type) {
      case SdParam::number:
	decl.addRange(min, count, parm.n);
	if (found && adjCount > 0) {
	  ISet<WideChar> baseMissing;
	  desc.addBaseRange(baseDesc, min, min + (adjCount - 1), parm.n,
			    baseMissing);
	  if (!baseMissing.isEmpty() && options().warnSgmlDecl)
	    message(ParserMessages::basesetCharsMissing,
		    CharsetMessageArg(baseMissing));
	}
	break;
      case SdParam::reservedName + Sd::rUNUSED:
	decl.addRange(min, count);
	break;
      case SdParam::minimumLiteral:
	{
	  UnivChar c = charNameToUniv(*sdBuilder.sd, parm.literalText.string());
	  if (adjCount > 256) {
	    message(ParserMessages::tooManyCharsMinimumLiteral);
	    adjCount = 256;
	  }
	  for (Number i = 0; i < adjCount; i++)
	    desc.addRange(min + i, min + i, c);
	}
	maybeISO646 = 0;
        decl.addRange(min, count, parm.literalText.string());
        break;
      default:
        CANNOT_HAPPEN();			 
      }
      SdParam::Type follow = (isDocument
			      ? SdParam::reservedName + Sd::rCAPACITY
			      : SdParam::reservedName + Sd::rFUNCTION);
      if (!parseSdParam(AllowedSdParams(SdParam::number,
					SdParam::reservedName + Sd::rBASESET,
					follow),
			parm))
	return 0;
	
    } while (parm.type == SdParam::number);
  } while (parm.type == SdParam::reservedName + Sd::rBASESET);
  if (!multiplyDeclared.isEmpty())
    message(ParserMessages::duplicateCharNumbers,
	    CharsetMessageArg(multiplyDeclared));
  ISet<WideChar> declaredSet;
  decl.declaredSet(declaredSet);
  ISetIter<WideChar> iter(declaredSet);
  WideChar min, max, lastMax;
  if (iter.next(min, max)) {
    ISet<WideChar> holes;
    lastMax = max;
    while (iter.next(min, max)) {
      if (min - lastMax > 1)
	holes.addRange(lastMax + 1, min - 1);
      lastMax = max;
    }
    if (!holes.isEmpty())
      message(ParserMessages::codeSetHoles, CharsetMessageArg(holes));
  }
  if (!isDocument && sdBuilder.sd->scopeInstance()) {
    // If scope is INSTANCE, syntax reference character set
    // must be same as reference.
    UnivCharsetDescIter iter(desc);
    WideChar descMin, descMax;
    UnivChar univMin;
    Char nextDescMin = 0;
    while (maybeISO646) {
      if (!iter.next(descMin, descMax, univMin)) {
	if (nextDescMin != 128)
	  maybeISO646 = 0;
	break;
      }
      if (descMin != nextDescMin || univMin != descMin)
	maybeISO646 = 0;
      nextDescMin = descMax + 1;
    }
    if (!maybeISO646)
      message(ParserMessages::scopeInstanceSyntaxCharset);
  }
  return 1;
}

Boolean Parser::sdParseExternalCharset(Sd &sd, UnivCharsetDesc &desc)
{
  SdParam parm;
  for (;;) {
    if (!parseSdParam(AllowedSdParams(SdParam::number, SdParam::eE),
		     parm))
      break;
    if (parm.type == SdParam::eE)
      return 1;
    WideChar min = parm.n;
    if (!parseSdParam(AllowedSdParams(SdParam::number), parm))
      break;
    Number count = parm.n;
    if (!parseSdParam(AllowedSdParams(SdParam::number,
				      SdParam::minimumLiteral,
				      SdParam::reservedName + Sd::rUNUSED),
		     parm))
      break;
    if (parm.type == SdParam::number) {
      if (count > 0)
	desc.addRange(min, min + (count - 1), parm.n);
    }
    else if (parm.type == SdParam::minimumLiteral) {
      UnivChar c = charNameToUniv(sd, parm.literalText.string());
      if (count > 256) {
	message(ParserMessages::tooManyCharsMinimumLiteral);
	count = 256;
      }
      for (Number i = 0; i < count; i++)
	desc.addRange(min + i, min + i, c);
    }
  }
  popInputStack();
  return 0;
}

UnivChar Parser::charNameToUniv(Sd &sd, const StringC &name)
{
  UnivChar univ;
  if (entityCatalog().lookupChar(name, sd.internalCharset(), messenger(), univ))
    return univ;
  else
    return sd.nameToUniv(name);
}

Boolean Parser::sdParseCapacity(SdBuilder &sdBuilder, SdParam &parm)
{
  if (!parseSdParam(sdBuilder.www
                    ? AllowedSdParams(SdParam::reservedName + Sd::rNONE,
				      SdParam::reservedName + Sd::rPUBLIC,
                                      SdParam::reservedName + Sd::rSGMLREF)
		    : AllowedSdParams(SdParam::reservedName + Sd::rPUBLIC,
				      SdParam::reservedName + Sd::rSGMLREF),
		    parm))
    return 0;
#if _MSC_VER == 1100
  // Workaround for Visual C++ 5.0 bug
  int
#else
  Boolean
#endif
    pushed = 0;
  if (parm.type == SdParam::reservedName + Sd::rNONE)
    return parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rSCOPE),
			parm);
  if (parm.type == SdParam::reservedName + Sd::rPUBLIC) {
    if (!parseSdParam(AllowedSdParams(SdParam::minimumLiteral), parm))
      return 0;
    PublicId id;
    PublicId::TextClass textClass;
    const MessageType1 *err;
    if (!id.init(parm.literalText, sd().internalCharset(), syntax().space(), err))
      sdBuilder.addFormalError(currentLocation(),
			       *err,
			       id.string());
    else if (id.getTextClass(textClass)
	     && textClass != PublicId::CAPACITY)
      sdBuilder.addFormalError(currentLocation(),
			       ParserMessages::capacityTextClass,
			       id.string());
    const StringC &str = id.string();
    if (str != sd().execToInternal("ISO 8879-1986//CAPACITY Reference//EN")
	&& str != sd().execToInternal("ISO 8879:1986//CAPACITY Reference//EN")) {
      Boolean givenError;
      if (referencePublic(id, PublicId::CAPACITY, givenError))
	pushed = 1;
      else if (!givenError)
	message(ParserMessages::unknownCapacitySet, StringMessageArg(str));
    }
    if (!pushed)
      return parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rSCOPE),
			  parm);
  }

  PackedBoolean capacitySpecified[Sd::nCapacity];
  int i;
  for (i = 0; i < Sd::nCapacity; i++)
    capacitySpecified[i] = 0;
  if (!parseSdParam(AllowedSdParams(SdParam::capacityName), parm))
    return 0;
  do {
    Sd::Capacity capacityIndex = parm.capacityIndex;
    if (!parseSdParam(AllowedSdParams(SdParam::number), parm))
      return 0;
    
    if (!capacitySpecified[capacityIndex]) {
      sdBuilder.sd->setCapacity(capacityIndex, parm.n);
      capacitySpecified[capacityIndex] = 1;
    }
    else if (options().warnSgmlDecl)
      message(ParserMessages::duplicateCapacity,
	      StringMessageArg(sd().capacityName(i)));
    int final = pushed ? int(SdParam::eE) : SdParam::reservedName + Sd::rSCOPE;
    if (!parseSdParam(AllowedSdParams(SdParam::capacityName, final),
		      parm))
      return 0;
  } while (parm.type == SdParam::capacityName);
  Number totalcap = sdBuilder.sd->capacity(0);
  for (i = 1; i < Sd::nCapacity; i++)
    if (sdBuilder.sd->capacity(i) > totalcap)
      message(ParserMessages::capacityExceedsTotalcap,
	      StringMessageArg(sd().capacityName(i)));
  if (pushed)
    return parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rSCOPE),
			parm);
  return 1;
}

Boolean Parser::referencePublic(const PublicId &id,
				PublicId::TextClass entityType,
				Boolean &givenError)
{
  givenError = 0;
  StringC sysid;
  if (entityCatalog().lookupPublic(id.string(),
				   sd().internalCharset(),
				   messenger(),
				   sysid)) {
    Location loc = currentLocation();
    eventHandler().sgmlDeclEntity(new (eventAllocator())
				  SgmlDeclEntityEvent(id,
						      entityType,
						      sysid,
						      loc));
    Ptr<EntityOrigin> origin(EntityOrigin::make(internalAllocator(),
						ConstPtr<Entity>(0),
						loc));
    if (currentMarkup())
      currentMarkup()->addEntityStart(origin);
    InputSource *in = entityManager().open(sysid,
					   sd().docCharset(),
					   origin.pointer(),
					   0,
					   messenger());
    if (!in) {
      givenError = 1;
      return 0;
    }
    pushInput(in);
    return 1;
  }
  return 0;
}

Boolean Parser::sdParseScope(SdBuilder &sdBuilder, SdParam &parm)
{
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rINSTANCE,
				    SdParam::reservedName + Sd::rDOCUMENT),
		    parm))
    return 0;
  if (parm.type == SdParam::reservedName + Sd::rINSTANCE)
    sdBuilder.sd->setScopeInstance();
  return 1;
}

Boolean Parser::sdParseSyntax(SdBuilder &sdBuilder, SdParam &parm)
{
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rSYNTAX),
		    parm))
    return 0;
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rSHUNCHAR,
				    SdParam::reservedName + Sd::rPUBLIC),
		    parm))
    return 0;

  if (parm.type == SdParam::reservedName + Sd::rPUBLIC) {
    if (!parseSdParam(AllowedSdParams(SdParam::minimumLiteral), parm))
      return 0;
    PublicId id;
    const MessageType1 *err;
    PublicId::TextClass textClass;
    if (!id.init(parm.literalText, sd().internalCharset(), syntax().space(), err))
      sdBuilder.addFormalError(currentLocation(),
			       *err,
			       id.string());
    else if (id.getTextClass(textClass)
	     && textClass != PublicId::SYNTAX)
      sdBuilder.addFormalError(currentLocation(),
			       ParserMessages::syntaxTextClass,
			       id.string());
    if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rFEATURES,
				      SdParam::reservedName + Sd::rSWITCHES),
		      parm))
      return 0;
    Vector<UnivChar> charSwitches;
    if (parm.type == SdParam::reservedName + Sd::rSWITCHES) {
      if (!parseSdParam(AllowedSdParams(SdParam::number), parm))
	return 0;
      for (;;) {
	SyntaxChar c = parm.n;
	if (!parseSdParam(AllowedSdParams(SdParam::number), parm))
	  return 0;
	sdBuilder.switcher.addSwitch(c, parm.n);
	if (!parseSdParam(AllowedSdParams(SdParam::number,
					  SdParam::reservedName
					  + Sd::rFEATURES),
			  parm))
	  return 0;
	if (parm.type != SdParam::number)
	  break;
      }
    }
    const StandardSyntaxSpec *spec = lookupSyntax(id);
    if (spec) {
      if (!setStandardSyntax(*sdBuilder.syntax,
			     *spec,
			     sdBuilder.sd->internalCharset(),
			     sdBuilder.switcher,
			     sdBuilder.www))
	sdBuilder.valid = 0;
    }
    else {
      Boolean givenError;
      if (referencePublic(id, PublicId::SYNTAX, givenError)) {
	sdBuilder.externalSyntax = 1;
	SdParam parm2;
	if (!parseSdParam(AllowedSdParams(SdParam::reservedName
					  + Sd::rSHUNCHAR),
			  parm2))
	  return 0;
	if (!sdParseExplicitSyntax(sdBuilder, parm2))
	  return 0;
      }
      else {
	if (!givenError)
	  message(ParserMessages::unknownPublicSyntax,
		  StringMessageArg(id.string()));
	sdBuilder.valid = 0;
      }
    }
  }
  else {
    if (!sdParseExplicitSyntax(sdBuilder, parm))
      return 0;
  }
  if (!sdBuilder.sd->scopeInstance()) {
    // we know the significant chars now
    ISet<WideChar> invalidSgmlChar;
    sdBuilder.syntax->checkSgmlChar(*sdBuilder.sd,
				    0,
				    1,
				    invalidSgmlChar);
    if (!invalidSgmlChar.isEmpty())
      message(ParserMessages::invalidSgmlChar, CharsetMessageArg(invalidSgmlChar));
  }
  checkSyntaxNamelen(*sdBuilder.syntax);
  checkSwitchesMarkup(sdBuilder.switcher);
  return 1;
}

Boolean Parser::sdParseExplicitSyntax(SdBuilder &sdBuilder,
				      SdParam &parm)
{
  typedef Boolean (Parser::*SdParser)(SdBuilder &, SdParam &);
  static SdParser parsers[] = {
    &Parser::sdParseShunchar,
    &Parser::sdParseSyntaxCharset,
    &Parser::sdParseFunction,
    &Parser::sdParseNaming,
    &Parser::sdParseDelim,
    &Parser::sdParseNames,
    &Parser::sdParseQuantity
    };
  for (size_t i = 0; i < SIZEOF(parsers); i++)
    if (!(this->*(parsers[i]))(sdBuilder, parm))
      return 0;
  return 1;
}

const StandardSyntaxSpec *Parser::lookupSyntax(const PublicId &id)
{
  PublicId::OwnerType ownerType;
  if (!id.getOwnerType(ownerType) || ownerType != PublicId::ISO)
    return 0;
  StringC str;
  if (!id.getOwner(str))
    return 0;
  if (str != sd().execToInternal("ISO 8879:1986")
      && str != sd().execToInternal("ISO 8879-1986"))
    return 0;
  PublicId::TextClass textClass;
  if (!id.getTextClass(textClass) || textClass != PublicId::SYNTAX)
    return 0;
  if (!id.getDescription(str))
    return 0;
  if (str == sd().execToInternal("Reference"))
    return &refSyntax;
  if (str == sd().execToInternal("Core"))
    return &coreSyntax;
  return 0;
}

Boolean Parser::sdParseSyntaxCharset(SdBuilder &sdBuilder, SdParam &parm)
{
  UnivCharsetDesc desc;
  if (!sdParseCharset(sdBuilder, parm, 0, sdBuilder.syntaxCharsetDecl, desc))
    return 0;
  sdBuilder.syntaxCharset.set(desc);
  checkSwitches(sdBuilder.switcher, sdBuilder.syntaxCharset);
  for (size_t i = 0; i < sdBuilder.switcher.nSwitches(); i++)
    if (!sdBuilder.syntaxCharsetDecl.charDeclared(sdBuilder.switcher.switchTo(i)))
      message(ParserMessages::switchNotInCharset,
	      NumberMessageArg(sdBuilder.switcher.switchTo(i)));
  ISet<WideChar> missing;
  findMissingMinimum(sdBuilder.syntaxCharset, missing);
  if (!missing.isEmpty())
    message(ParserMessages::missingMinimumChars,
	    CharsetMessageArg(missing));
  return 1;
}

Boolean Parser::sdParseShunchar(SdBuilder &sdBuilder, SdParam &parm)
{
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rNONE,
				    SdParam::reservedName + Sd::rCONTROLS,
				    SdParam::number), parm))
    return 0;
  if (parm.type == SdParam::reservedName + Sd::rNONE) {
    if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rBASESET),
		      parm))
      return 0;
    return 1;
  }
  if (parm.type == SdParam::reservedName + Sd::rCONTROLS)
    sdBuilder.syntax->setShuncharControls();
  else {
    if (parm.n <= charMax)
      sdBuilder.syntax->addShunchar(Char(parm.n));
  }
  for (;;) {
    if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rBASESET,
				      SdParam::number), parm))
      return 0;
    if (parm.type != SdParam::number)
      break;
    if (parm.n <= charMax)
      sdBuilder.syntax->addShunchar(Char(parm.n));
  }
  return 1;
}

Boolean Parser::sdParseFunction(SdBuilder &sdBuilder, SdParam &parm)
{
  static Sd::ReservedName standardNames[3] = {
    Sd::rRE, Sd::rRS, Sd::rSPACE
  };
  for (int i = 0; i < 3; i++) {
    if (!parseSdParam(AllowedSdParams(SdParam::reservedName
				      + standardNames[i]),
		      parm))
      return 0;
    if (!parseSdParam(AllowedSdParams(SdParam::number), parm))
      return 0;
    Char c;
    if (translateSyntax(sdBuilder, parm.n, c)) {
      if (checkNotFunction(*sdBuilder.syntax, c))
	sdBuilder.syntax->setStandardFunction(Syntax::StandardFunction(i), c);
      else
	sdBuilder.valid = 0;
    }
  }
  Boolean haveMsichar = 0;
  Boolean haveMsochar = 0;
  for (;;) {
    if (!parseSdParam(sdBuilder.externalSyntax
		      ? AllowedSdParams(SdParam::name, SdParam::paramLiteral)
		      : AllowedSdParams(SdParam::name),
		      parm))
      return 0;
    Boolean nameWasLiteral;
    size_t nameMarkupIndex;
    if (currentMarkup())
      nameMarkupIndex = currentMarkup()->size() - 1;
    Boolean invalidName = 0;
    StringC name;
    if (parm.type == SdParam::paramLiteral) {
      nameWasLiteral = 1;
      if (!translateSyntax(sdBuilder, parm.paramLiteralText, name))
	invalidName = 1;
    }
    else {
      parm.token.swap(name);
      nameWasLiteral = 0;
    }
    if (!parseSdParam(nameWasLiteral
		      ? AllowedSdParams(SdParam::reservedName + Sd::rFUNCHAR,
					SdParam::reservedName + Sd::rMSICHAR,
					SdParam::reservedName + Sd::rMSOCHAR,
					SdParam::reservedName + Sd::rMSSCHAR,
					SdParam::reservedName + Sd::rSEPCHAR)
		      : AllowedSdParams(SdParam::reservedName + Sd::rFUNCHAR,
					SdParam::reservedName + Sd::rMSICHAR,
					SdParam::reservedName + Sd::rMSOCHAR,
					SdParam::reservedName + Sd::rMSSCHAR,
					SdParam::reservedName + Sd::rSEPCHAR,
					SdParam::reservedName + Sd::rLCNMSTRT),
		      parm))
      return 0;
    if (parm.type == SdParam::reservedName + Sd::rLCNMSTRT) {
      if (name != sd().reservedName(Sd::rNAMING))
	message(ParserMessages::namingBeforeLcnmstrt,
		StringMessageArg(name));
      else if (currentMarkup())
	currentMarkup()->changeToSdReservedName(nameMarkupIndex, Sd::rNAMING);
      break;
    }
    if (!nameWasLiteral) {
      StringC tem;
      name.swap(tem);
      if (!translateName(sdBuilder, tem, name))
	invalidName = 1;
    }
    Syntax::FunctionClass functionClass;
    switch (parm.type) {
    case SdParam::reservedName + Sd::rFUNCHAR:
      functionClass = Syntax::cFUNCHAR;
      break;
    case SdParam::reservedName + Sd::rMSICHAR:
      haveMsichar = 1;
      functionClass = Syntax::cMSICHAR;
      break;
    case SdParam::reservedName + Sd::rMSOCHAR:
      haveMsochar = 1;
      functionClass = Syntax::cMSOCHAR;
      break;
    case SdParam::reservedName + Sd::rMSSCHAR:
      functionClass = Syntax::cMSSCHAR;
      break;
    case SdParam::reservedName + Sd::rSEPCHAR:
      functionClass = Syntax::cSEPCHAR;
      break;
    default:
      CANNOT_HAPPEN();
    }
    if (!parseSdParam(AllowedSdParams(SdParam::number), parm))
      return 0;
    Char c;
    if (translateSyntax(sdBuilder, parm.n, c)
        && checkNotFunction(*sdBuilder.syntax, c)
	&& !invalidName) {
      Char tem;
      if (sdBuilder.syntax->lookupFunctionChar(name, &tem))
	message(ParserMessages::duplicateFunctionName, StringMessageArg(name));
      else
	sdBuilder.syntax->addFunctionChar(name, functionClass, c);
    }
  }
  if (haveMsochar && !haveMsichar)
    message(ParserMessages::msocharRequiresMsichar);
  return 1;
}

Boolean Parser::sdParseNaming(SdBuilder &sdBuilder, SdParam &parm)
{
  static Sd::ReservedName keys[6] = {
    Sd::rUCNMSTRT, Sd::rNAMESTRT, Sd::rLCNMCHAR, Sd::rUCNMCHAR, Sd::rNAMECHAR,
    Sd::rNAMECASE
  };
  int isNamechar = 0;
  ISet<Char> nameStartChar;
  ISet<Char> nameChar;
  do {
    String<SyntaxChar> lc;
    Vector<size_t> rangeIndex;
    enum PrevParam {
      paramNone,
      paramNumber,
      paramOther
    } prevParam = paramNone;
      
    for (;;) {
      switch (prevParam) {
      case paramNone:
	if (!parseSdParam(AllowedSdParams(SdParam::paramLiteral, SdParam::number),
	                  parm))
	  return 0;
	break;
      case paramNumber:
	if (!parseSdParam(AllowedSdParams(SdParam::reservedName
					  + keys[isNamechar * 3],
					  SdParam::paramLiteral,
					  SdParam::number,
					  SdParam::minus),
			  parm))
	  return 0;
	break;
      case paramOther:
	if (!parseSdParam(AllowedSdParams(SdParam::reservedName
					  + keys[isNamechar * 3],
					  SdParam::paramLiteral,
					  SdParam::number),
			  parm))
	  return 0;
	break;
      }
      switch (parm.type) {
      case SdParam::paramLiteral:
	if (prevParam == paramNone)
	  break;
	// fall through
      case SdParam::number:
	if (!sdBuilder.externalSyntax && !sdBuilder.enr) {
	  message(ParserMessages::enrRequired);
	  sdBuilder.enr = 1;
	}
	break;
      default:
	break;
      }
      prevParam = (parm.type == SdParam::number ? paramNumber : paramOther);
      if (parm.type == SdParam::minus) {
	if (!parseSdParam(AllowedSdParams(SdParam::number),
			  parm))
	  return 0;
	if (parm.n < lc[lc.size() - 1])
	  message(ParserMessages::sdInvalidRange);
	else {
	  if (parm.n > lc[lc.size() - 1] + 1)
	    rangeIndex.push_back(lc.size() - 1);
	  lc += SyntaxChar(parm.n);
	}
      }
      else {
	sdParamConvertToLiteral(parm);
	if (parm.type != SdParam::paramLiteral)
	  break;
	lc += parm.paramLiteralText;
      }
    }
    size_t lcPos = 0;
    size_t rangeIndexPos = 0;
    unsigned long rangeLeft = 0;
    SyntaxChar nextRangeChar;
    ISet<Char> &set = isNamechar ? nameChar : nameStartChar;
    String<SyntaxChar> chars;
    Boolean runOut = 0;
    prevParam = paramNone;
    for (;;) {
      switch (prevParam) {
      case paramNone:
	if (!parseSdParam(AllowedSdParams(SdParam::paramLiteral, SdParam::number),
	                  parm))
	  return 0;
	break;
      case paramNumber:
	if (!parseSdParam(AllowedSdParams(SdParam::reservedName
					  + keys[isNamechar * 3 + 1],
					  SdParam::reservedName
					  + keys[isNamechar * 3 + 2],
					  SdParam::paramLiteral,
					  SdParam::number,
					  SdParam::minus),
			  parm))
	  return 0;
	break;
      case paramOther:
	if (!parseSdParam(AllowedSdParams(SdParam::reservedName
					  + keys[isNamechar * 3 + 1],
					  SdParam::reservedName
					  + keys[isNamechar * 3 + 2],
					  SdParam::paramLiteral,
					  SdParam::number),
			  parm))
	  return 0;
	break;
      }
      switch (parm.type) {
      case SdParam::paramLiteral:
	if (prevParam == paramNone)
	  break;
	// fall through
      case SdParam::number:
	if (!sdBuilder.externalSyntax && !sdBuilder.enr) {
	  message(ParserMessages::enrRequired);
	  sdBuilder.enr = 1;
	}
	break;
      default:
	break;
      }
      prevParam = (parm.type == SdParam::number ? paramNumber : paramOther);
      if (parm.type == SdParam::minus) {
	if (!parseSdParam(AllowedSdParams(SdParam::number),
			  parm))
	  return 0;
	ASSERT(chars.size() == 1);
	SyntaxChar start = chars[0];
  	SyntaxChar end = parm.n;
	if (start > end)
	  message(ParserMessages::sdInvalidRange);
	else {
	  size_t count = end + 1 - start;
	  while (count > 0) {
	    if (rangeLeft == 0
		&& rangeIndexPos < rangeIndex.size()
		&& rangeIndex[rangeIndexPos] == lcPos) {
	      rangeLeft = 1 + lc[lcPos + 1] - lc[lcPos];
	      nextRangeChar = lc[lcPos];
	      lcPos += 2;
	      rangeIndexPos += 1;
	    }
	    Char c;
	    if (rangeLeft > 0) {
	      rangeLeft--;
	      c = nextRangeChar++;
	    }
	    else if (lcPos < lc.size())
	      c = lc[lcPos++];
	    else {
	      c = start;
	      runOut = 1;
	    }
	    if (c == start && count > 1 && (runOut || rangeLeft > 0)) {
	      size_t n;
	      if (runOut)
		n = count;
	      else if (rangeLeft < count) {
		// rangeLeft + 1 <= count
		n = rangeLeft + 1;
		rangeLeft = 0;
	      }
	      else {
		// count < rangeLeft + 1
		n = count;
		rangeLeft -= n - 1;
		nextRangeChar += n - 1;
	      }
	      translateRange(sdBuilder, start, start + (n - 1), set);
	      count -= n;
	      start += n;
	    }
	    else {
	      Char transLc, transUc;
	      if (translateSyntax(sdBuilder, c, transLc)
		  && translateSyntax(sdBuilder, start, transUc)) {
		set.add(transLc);
		if (transLc != transUc) {
		  set.add(transUc);
		  sdBuilder.syntax->addSubst(transLc, transUc);
		}
	      }
	      count--;
	      start++;
	    }
	  } 
	}
	chars.resize(0);
      }
      else {
	for (size_t i = 0; i < chars.size(); i++) {
	  if (rangeLeft == 0
	      && rangeIndexPos < rangeIndex.size()
	      && rangeIndex[rangeIndexPos] == lcPos) {
	    rangeLeft = 1 + lc[lcPos + 1] - lc[lcPos];
	    nextRangeChar = lc[lcPos];
	    lcPos += 2;
	    rangeIndexPos += 1;
	  }
	  Char c;
	  if (rangeLeft > 0) {
	    rangeLeft--;
	    c = nextRangeChar++;
	  }
	  else if (lcPos < lc.size())
	    c = lc[lcPos++];
	  else {
	    runOut = 1;
	    c = chars[i];
	  }
	  // map from c to chars[i]
	  Char transLc, transUc;
	  if (translateSyntax(sdBuilder, c, transLc)
	      && translateSyntax(sdBuilder, chars[i], transUc)) {
	    set.add(transLc);
	    if (transLc != transUc) {
	      set.add(transUc);
	      sdBuilder.syntax->addSubst(transLc, transUc);
	    }
	  }
	}
	sdParamConvertToLiteral(parm);
	if (parm.type != SdParam::paramLiteral)
	  break;
	parm.paramLiteralText.swap(chars);
      }
    }
    if ((runOut && !sdBuilder.externalSyntax)
	|| rangeLeft > 0 || lcPos < lc.size())
      message(isNamechar
	      ? ParserMessages::nmcharLength
	      : ParserMessages::nmstrtLength);
    if (parm.type == SdParam::reservedName + keys[isNamechar * 3 + 1]) {
      if (!sdBuilder.externalSyntax && !sdBuilder.enr) {
	message(ParserMessages::enrRequired);
	sdBuilder.enr = 1;
      }
      prevParam = paramNone;
      for (;;) {
	switch (prevParam) {
	case paramNone:
	  if (!parseSdParam(AllowedSdParams(SdParam::paramLiteral, SdParam::number),
			    parm))
	    return 0;
	  break;
	case paramNumber:
	  if (!parseSdParam(AllowedSdParams(SdParam::reservedName
					    + keys[isNamechar * 3 + 2],
					    SdParam::paramLiteral,
					    SdParam::number,
					    SdParam::minus),
			    parm))
	    return 0;
	  break;
	case paramOther:
	  if (!parseSdParam(AllowedSdParams(SdParam::reservedName
					    + keys[isNamechar * 3 + 2],
					    SdParam::paramLiteral,
					    SdParam::number),
			    parm))
	    return 0;
	  break;
	}
	prevParam = (parm.type == SdParam::number ? paramNumber : paramOther);
	if (parm.type == SdParam::minus) {
	  SyntaxChar prevNumber = parm.n;
	  if (!parseSdParam(AllowedSdParams(SdParam::number),
			    parm))
	    return 0;
	  if (parm.n < prevNumber)
	    message(ParserMessages::sdInvalidRange);
	  else if (parm.n > prevNumber)
	    translateRange(sdBuilder, prevNumber + 1, parm.n, set);
	}
	else {
	  sdParamConvertToLiteral(parm);
	  if (parm.type != SdParam::paramLiteral)
	    break;
	  for (size_t i = 0; i < parm.paramLiteralText.size(); i++) {
	    Char trans;
	    if (translateSyntax(sdBuilder, parm.paramLiteralText[i], trans))
	      set.add(trans);
	  }
	}
      }
    }
    if (!checkNmchars(set, *sdBuilder.syntax))
      sdBuilder.valid = 0;
  } while (!isNamechar++);
  ISet<WideChar> bad;
  intersectCharSets(nameStartChar, nameChar, bad);
  if (!bad.isEmpty()) {
    sdBuilder.valid = 0;
    message(ParserMessages::nmcharNmstrt, CharsetMessageArg(bad));
  }
  sdBuilder.syntax->addNameStartCharacters(nameStartChar);
  sdBuilder.syntax->addNameCharacters(nameChar);
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rGENERAL),
		    parm))
    return 0;
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rNO,
				    SdParam::reservedName + Sd::rYES),
		    parm))
    return 0;
  sdBuilder.syntax->setNamecaseGeneral(parm.type
				       == SdParam::reservedName + Sd::rYES);

  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rENTITY),
		    parm))
    return 0;
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rNO,
				    SdParam::reservedName + Sd::rYES),
		    parm))
    return 0;
  sdBuilder.syntax->setNamecaseEntity(parm.type
				      == SdParam::reservedName + Sd::rYES);
  return 1;
}

Boolean Parser::checkNmchars(const ISet<Char> &set, const Syntax &syntax)
{
  Boolean valid = 1;
  ISet<WideChar> bad;
  intersectCharSets(set, *syntax.charSet(Syntax::nameStart), bad);
  if (!bad.isEmpty()) {
    message(ParserMessages::nmcharLetter, CharsetMessageArg(bad));
    valid = 0;
    bad.clear();
  }
  intersectCharSets(set, *syntax.charSet(Syntax::digit), bad);
  if (!bad.isEmpty()) {
    message(ParserMessages::nmcharDigit, CharsetMessageArg(bad));
    valid = 0;
    bad.clear();
  }
  Char funChar;
  if (syntax.getStandardFunction(Syntax::fRE, funChar)
      && set.contains(funChar)) {
    message(ParserMessages::nmcharRe, NumberMessageArg(funChar));
    valid = 0;
  }
  if (syntax.getStandardFunction(Syntax::fRS, funChar)
      && set.contains(funChar)) {
    message(ParserMessages::nmcharRs, NumberMessageArg(funChar));
    valid = 0;
  }
  if (syntax.getStandardFunction(Syntax::fSPACE, funChar)
      && set.contains(funChar)) {
    message(ParserMessages::nmcharSpace, NumberMessageArg(funChar));
    valid = 0;
  }
  intersectCharSets(set, *syntax.charSet(Syntax::sepchar), bad);
  if (!bad.isEmpty()) {
    message(ParserMessages::nmcharSepchar, CharsetMessageArg(bad));
    valid = 0;
  }
  return valid;
}

// Result is a ISet<WideChar>, so it can be used with CharsetMessageArg.

void Parser::intersectCharSets(const ISet<Char> &s1, const ISet<Char> &s2,
			       ISet<WideChar> &inter)
{
  ISetIter<Char> i1(s1);
  ISetIter<Char> i2(s2);
  Char min1, max1, min2, max2;
  if (!i1.next(min1, max1))
    return;
  if (!i2.next(min2, max2))
    return;
  for (;;) {
    if (max1 < min2) {
      if (!i1.next(min1, max1))
	break;
    }
    else if (max2 < min1) {
      if (!i2.next(min2, max2))
	break;
    }
    else {
      // min2 <= max1
      // min1 <= max2
      Char min = min1 > min2 ? min1 : min2;
      Char max = max1 < max2 ? max1 : max2;
      inter.addRange(min, max);
      if (max2 > max) {
	if (!i1.next(min1, max1))
	  break;
      }
      else {
	if (!i2.next(min2, max2))
	  break;
      }
    }
  }
}

Boolean Parser::sdParseDelim(SdBuilder &sdBuilder, SdParam &parm)
{
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rDELIM),
		    parm))
    return 0;
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rGENERAL),
		    parm))
    return 0;
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rSGMLREF),
		    parm))
    return 0;
  PackedBoolean delimGeneralSpecified[Syntax::nDelimGeneral];
  for (int i = 0; i < Syntax::nDelimGeneral; i++)
    delimGeneralSpecified[i] = 0;
  for (;;) {
    if (!parseSdParam(AllowedSdParams(SdParam::generalDelimiterName,
				      SdParam::reservedName + Sd::rSHORTREF),
		      parm))
      return 0;
    if (parm.type == SdParam::reservedName + Sd::rSHORTREF)
      break;
    Syntax::DelimGeneral delimGeneral = parm.delimGeneralIndex;
    if (delimGeneralSpecified[delimGeneral])
      message(ParserMessages::duplicateDelimGeneral,
	      StringMessageArg(sd().generalDelimiterName(delimGeneral)));
    switch (delimGeneral) {
    case Syntax::dHCRO:
    case Syntax::dNESTC:
      requireWWW(sdBuilder);
      break;
    default:
      break;
    }
    if (!parseSdParam(sdBuilder.externalSyntax
		      ? AllowedSdParams(SdParam::paramLiteral,
					SdParam::number)
		      : AllowedSdParams(SdParam::paramLiteral),
		      parm))
      return 0;
    sdParamConvertToLiteral(parm);
    StringC str;
    if (parm.paramLiteralText.size() == 0)
	message(ParserMessages::sdEmptyDelimiter);
    else if (translateSyntax(sdBuilder, parm.paramLiteralText, str)) {
      const SubstTable<Char> *table = sdBuilder.syntax->generalSubstTable();
      for (size_t i = 0; i < str.size(); i++)
	table->subst(str[i]);
      if (checkGeneralDelim(*sdBuilder.syntax, str)
	  && !delimGeneralSpecified[delimGeneral])
	sdBuilder.syntax->setDelimGeneral(delimGeneral, str);
      else
	sdBuilder.valid = 0;
    }
    delimGeneralSpecified[delimGeneral] = 1;
  }
  if (sdBuilder.syntax->delimGeneral(Syntax::dNET).size()
      && !sdBuilder.syntax->delimGeneral(Syntax::dNESTC).size())
    sdBuilder.syntax->setDelimGeneral(Syntax::dNESTC,
				      sdBuilder.syntax->delimGeneral(Syntax::dNET));
  if (!setRefDelimGeneral(*sdBuilder.syntax,
			  sdBuilder.syntaxCharset,
			  sdBuilder.sd->internalCharset(),
			  sdBuilder.switcher))
    sdBuilder.valid = 0;
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rSGMLREF,
				    SdParam::reservedName + Sd::rNONE),
		    parm))
    return 0;
  if (parm.type == SdParam::reservedName + Sd::rSGMLREF) {
    if (!addRefDelimShortref(*sdBuilder.syntax,
			     sdBuilder.syntaxCharset,
			     sdBuilder.sd->internalCharset(),
			     sdBuilder.switcher))
      sdBuilder.valid = 0;
  }
  String<SyntaxChar> lastLiteral;
  for (;;) {
    if (!parseSdParam(sdBuilder.externalSyntax
		      ? AllowedSdParams(SdParam::paramLiteral,
					SdParam::number,
					SdParam::minus,
					SdParam::reservedName + Sd::rNAMES)
		      : AllowedSdParams(SdParam::paramLiteral,
					SdParam::reservedName + Sd::rNAMES),
		      parm))
      return 0;
    sdParamConvertToLiteral(parm);
    if (parm.type == SdParam::minus) {
      if (!parseSdParam(AllowedSdParams(SdParam::paramLiteral,
					SdParam::number),
			parm))
	return 0;
      sdParamConvertToLiteral(parm);
      if (parm.paramLiteralText.size() == 0)
	message(ParserMessages::sdEmptyDelimiter);
      else if (lastLiteral.size() != 1
	       || parm.paramLiteralText.size() != 1)
	message(ParserMessages::sdRangeNotSingleChar);
      else if (parm.paramLiteralText[0] < lastLiteral[0])
	message(ParserMessages::sdInvalidRange);
      else if (parm.paramLiteralText[0] != lastLiteral[0]) {
	ISet<Char> shortrefChars;
	translateRange(sdBuilder,
		       lastLiteral[0] + 1,
		       parm.paramLiteralText[0],
		       shortrefChars);
	ISet<WideChar> duplicates;
	intersectCharSets(shortrefChars,
			  sdBuilder.syntax->delimShortrefSimple(),
			  duplicates);
	int nComplexShortrefs = sdBuilder.syntax->nDelimShortrefComplex();
	for (int i = 0; i < nComplexShortrefs; i++) {
	  const StringC &delim = sdBuilder.syntax->delimShortrefComplex(i);
	  if (delim.size() == 1 && shortrefChars.contains(delim[0]))
	    duplicates.add(delim[0]);
	}
	if (!duplicates.isEmpty())
	  message(ParserMessages::duplicateDelimShortrefSet,
		  CharsetMessageArg(duplicates));
	sdBuilder.syntax->addDelimShortrefs(shortrefChars,
					    sdBuilder.sd->internalCharset());
      }
      lastLiteral.resize(0);
    }
    else if (parm.type == SdParam::paramLiteral) {
      parm.paramLiteralText.swap(lastLiteral);
      StringC str;
      if (lastLiteral.size() == 0)
	message(ParserMessages::sdEmptyDelimiter);
      else if (translateSyntax(sdBuilder, lastLiteral, str)) {
	const SubstTable<Char> *table = sdBuilder.syntax->generalSubstTable();
	for (size_t i = 0; i < str.size(); i++)
	  table->subst(str[i]);
	if (str.size() == 1
	    || checkShortrefDelim(*sdBuilder.syntax,
				  sdBuilder.sd->internalCharset(),
				  str)) {
	  if (sdBuilder.syntax->isValidShortref(str))
	    message(ParserMessages::duplicateDelimShortref,
		    StringMessageArg(str));
	  else
	    sdBuilder.syntax->addDelimShortref(str,
					       sdBuilder.sd->internalCharset());
	}
      }
    }
    else
      break;
  }
  return 1;
}

Boolean Parser::sdParseNames(SdBuilder &sdBuilder, SdParam &parm)
{
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rSGMLREF),
		    parm))
    return 0;
  for (;;) {
    if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rQUANTITY,
				      SdParam::referenceReservedName),
		      parm))
      return 0;
    if (parm.type == SdParam::reservedName + Sd::rQUANTITY)
      break;
    Syntax::ReservedName reservedName = parm.reservedNameIndex;
    switch (reservedName) {
    case Syntax::rALL:
    case Syntax::rDATA:
    case Syntax::rIMPLICIT:
      requireWWW(sdBuilder);
      break;
    default:
      break;
    }
    if (!parseSdParam(sdBuilder.externalSyntax
		      ? AllowedSdParams(SdParam::name, SdParam::paramLiteral)
		      : AllowedSdParams(SdParam::name),
		      parm))
      return 0;
    StringC transName;
    if (parm.type == SdParam::name
	? translateName(sdBuilder, parm.token, transName)
	: translateSyntax(sdBuilder, parm.paramLiteralText, transName)) {
      Syntax::ReservedName tem;
      if (sdBuilder.syntax->lookupReservedName(transName, &tem))
	message(ParserMessages::ambiguousReservedName,
		StringMessageArg(transName));
      else {
	if (transName.size() == 0
	    || !sdBuilder.syntax->isNameStartCharacter(transName[0])) {
	  message(ParserMessages::reservedNameSyntax,
		  StringMessageArg(transName));
	  transName.resize(0);
	}
	size_t i;
	// Check that its a valid name in the declared syntax
	// (- and . might not be name characters).
	for (i = 1; i < transName.size(); i++)
	  if (!sdBuilder.syntax->isNameCharacter(transName[i])) {
	    message(ParserMessages::reservedNameSyntax,
		    StringMessageArg(transName));
	    transName.resize(0);
	    break;
	  }
	for (i = 0; i < transName.size(); i++)
	  sdBuilder.syntax->generalSubstTable()->subst(transName[i]);
	if (sdBuilder.syntax->reservedName(reservedName).size() > 0)
	  message(ParserMessages::duplicateReservedName,
		  StringMessageArg(syntax().reservedName(reservedName)));
	else if (transName.size() > 0)
	  sdBuilder.syntax->setName(reservedName, transName);
	else
	  sdBuilder.valid = 0;
      }
    }
  }
  setRefNames(*sdBuilder.syntax, sdBuilder.sd->internalCharset(), sdBuilder.www);
  static Syntax::ReservedName functionNameIndex[3] = {
    Syntax::rRE, Syntax::rRS, Syntax::rSPACE
  };
  for (int i = 0; i < 3; i++) {
    const StringC &functionName
      = sdBuilder.syntax->reservedName(functionNameIndex[i]);
    Char tem;
    if (sdBuilder.syntax->lookupFunctionChar(functionName, &tem))
      message(ParserMessages::duplicateFunctionName, StringMessageArg(functionName));
  }
  sdBuilder.syntax->enterStandardFunctionNames();
  return 1;
}

Boolean Parser::sdParseQuantity(SdBuilder &sdBuilder, SdParam &parm)
{
  if (!parseSdParam(sdBuilder.www
                    ? AllowedSdParams(SdParam::reservedName + Sd::rNONE,
                                      SdParam::reservedName + Sd::rSGMLREF)
		    : AllowedSdParams(SdParam::reservedName + Sd::rSGMLREF),
		    parm))
    return 0;
  int final = (sdBuilder.externalSyntax
	       ? int(SdParam::eE)
	       : SdParam::reservedName + Sd::rFEATURES);
  if (parm.type == SdParam::reservedName + Sd::rNONE) {
    for (int i = 0; i < Syntax::nQuantity; i++) {
      if (i != Syntax::qNORMSEP)
        sdBuilder.syntax->setQuantity(Syntax::Quantity(i), Syntax::unlimited);
    }
    if (!parseSdParam(AllowedSdParams(final, SdParam::reservedName + Sd::rENTITIES), parm))
      return 0;
  }
  else {
    for (;;) {
      if (!parseSdParam(sdBuilder.www
  		        ? AllowedSdParams(SdParam::quantityName,
					  final,
					  SdParam::reservedName + Sd::rENTITIES)
		        : AllowedSdParams(SdParam::quantityName, final),
		        parm))
        return 0;
      if (parm.type != SdParam::quantityName)
        break;
      Syntax::Quantity quantity = parm.quantityIndex;
      if (!parseSdParam(AllowedSdParams(SdParam::number), parm))
	return 0;
      sdBuilder.syntax->setQuantity(quantity, parm.n);
    }
    if (sdBuilder.sd->scopeInstance()) {
      for (int i = 0; i < Syntax::nQuantity; i++)
        if (sdBuilder.syntax->quantity(Syntax::Quantity(i))
	    < syntax().quantity(Syntax::Quantity(i)))
	   message(ParserMessages::scopeInstanceQuantity,
		   StringMessageArg(sd().quantityName(Syntax::Quantity(i))));
    }
  }
  if (parm.type == SdParam::reservedName + Sd::rENTITIES)
    return sdParseEntities(sdBuilder, parm);
  else
    return 1;
}

Boolean Parser::sdParseEntities(SdBuilder &sdBuilder, SdParam &parm)
{
  int final = (sdBuilder.externalSyntax
	       ? int(SdParam::eE)
	       : SdParam::reservedName + Sd::rFEATURES);
  for (;;) {
    if (!parseSdParam(AllowedSdParams(final, SdParam::paramLiteral), parm))
      return 0;
    if (parm.type != SdParam::paramLiteral)
      break;
    StringC name;
    if (!translateSyntax(sdBuilder, parm.paramLiteralText, name))
      name.resize(0);
    else if (name.size() == 0
	     || !sdBuilder.syntax->isNameStartCharacter(name[0])) {
      message(ParserMessages::entityNameSyntax, StringMessageArg(name));
      name.resize(0);
    }
    else {
      // Check that its a valid name in the declared syntax
      for (size_t i = 1; i < name.size(); i++)
	if (!sdBuilder.syntax->isNameCharacter(name[i])) {
	  message(ParserMessages::entityNameSyntax, StringMessageArg(name));
	  name.resize(0);
	  break;
	}
    }
    if (!parseSdParam(AllowedSdParams(SdParam::number), parm))
      return 0;
    Char c;
    if (translateSyntax(sdBuilder, parm.n, c) && name.size())
      sdBuilder.syntax->addEntity(name, c);
  }
  return 1;
}

Boolean Parser::sdParseFeatures(SdBuilder &sdBuilder, SdParam &parm)
{
  struct FeatureInfo {
    Sd::ReservedName name;
    enum {
      none,
      boolean,
      number,
      netenabl
    } arg;
  };
  static FeatureInfo features[] = {
    { Sd::rMINIMIZE, FeatureInfo::none },
    { Sd::rDATATAG, FeatureInfo::boolean },
    { Sd::rOMITTAG, FeatureInfo::boolean },
    { Sd::rRANK, FeatureInfo::boolean },
    { Sd::rSHORTTAG, FeatureInfo::none },
    { Sd::rSTARTTAG, FeatureInfo::none },
    { Sd::rEMPTY, FeatureInfo::boolean },
    { Sd::rUNCLOSED, FeatureInfo::boolean },
    { Sd::rNETENABL, FeatureInfo::netenabl },
    { Sd::rENDTAG, FeatureInfo::none },
    { Sd::rEMPTY, FeatureInfo::boolean },
    { Sd::rUNCLOSED, FeatureInfo::boolean },
    { Sd::rATTRIB, FeatureInfo::none },
    { Sd::rDEFAULT, FeatureInfo::boolean },
    { Sd::rOMITNAME, FeatureInfo::boolean },
    { Sd::rVALUE, FeatureInfo::boolean },
    { Sd::rEMPTYNRM, FeatureInfo::boolean },
    { Sd::rIMPLYDEF, FeatureInfo::none },
    { Sd::rATTLIST, FeatureInfo::boolean },
    { Sd::rDOCTYPE, FeatureInfo::boolean },
    { Sd::rELEMENT, FeatureInfo::boolean },
    { Sd::rENTITY, FeatureInfo::boolean },
    { Sd::rNOTATION, FeatureInfo::boolean },
    { Sd::rLINK, FeatureInfo::none },
    { Sd::rSIMPLE, FeatureInfo::number },
    { Sd::rIMPLICIT, FeatureInfo::boolean },
    { Sd::rEXPLICIT, FeatureInfo::number },
    { Sd::rOTHER, FeatureInfo::none },
    { Sd::rCONCUR, FeatureInfo::number },
    { Sd::rSUBDOC, FeatureInfo::number },
    { Sd::rFORMAL, FeatureInfo::boolean },
    { Sd::rURN, FeatureInfo::boolean },
    { Sd::rKEEPRSRE, FeatureInfo::boolean },
    { Sd::rVALIDITY, FeatureInfo::none },
  };
  int booleanFeature = 0;
  int numberFeature = 0;
  for (size_t i = 0; i < SIZEOF(features); i++) {
    switch (features[i].name) {
    case Sd::rSTARTTAG:
      // SHORTTAG
      if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rSTARTTAG,
					SdParam::reservedName + Sd::rNO,
					SdParam::reservedName + Sd::rYES),
			parm))
	return 0;
      if (parm.type == SdParam::reservedName + Sd::rSTARTTAG)
	break;
      sdBuilder.sd->setShorttag(parm.type == SdParam::reservedName + Sd::rYES);
      while (features[++i].name != Sd::rEMPTYNRM)
	if (features[i].arg == FeatureInfo::boolean)
	  booleanFeature++;
      // fall through
    case Sd::rEMPTYNRM:
      if (!parseSdParam(AllowedSdParams(SdParam::reservedName
				        + features[i].name,
				        SdParam::reservedName
				        + features[i + 7].name), parm))
	return 0;
      if (parm.type == SdParam::reservedName + features[i].name)
	requireWWW(sdBuilder);
      else {
	booleanFeature += 6;
	i += 7;
      }
      break;
    case Sd::rURN:
      if (!parseSdParam(AllowedSdParams(SdParam::reservedName + features[i].name,
	                                SdParam::reservedName + Sd::rAPPINFO), parm))
	return 0;
      if (parm.type == SdParam::reservedName + Sd::rAPPINFO)
	return 1;
      requireWWW(sdBuilder);
      break;
    default:
      if (!parseSdParam(AllowedSdParams(SdParam::reservedName
				        + features[i].name), parm))
	return 0;
      break;
    }
    switch (features[i].arg) {
    case FeatureInfo::number:
      if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rNO,
					SdParam::reservedName + Sd::rYES),
			parm))
	return 0;
      if (parm.type == SdParam::reservedName + Sd::rYES) {
	if (!parseSdParam(AllowedSdParams(SdParam::number), parm))
	  return 0;
	sdBuilder.sd->setNumberFeature(Sd::NumberFeature(numberFeature++),
				       parm.n);
      }
      else
	sdBuilder.sd->setNumberFeature(Sd::NumberFeature(numberFeature++),
				       0);
      break;
    case FeatureInfo::netenabl:
      if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rNO,
					SdParam::reservedName + Sd::rIMMEDNET,
					SdParam::reservedName + Sd::rALL),
			parm))
	return 0;
      switch (parm.type) {
      case SdParam::reservedName + Sd::rNO:
	sdBuilder.sd->setStartTagNetEnable(Sd::netEnableNo);
	break;
      case SdParam::reservedName + Sd::rIMMEDNET:
	sdBuilder.sd->setStartTagNetEnable(Sd::netEnableImmednet);
	break;
      case SdParam::reservedName + Sd::rALL:
	sdBuilder.sd->setStartTagNetEnable(Sd::netEnableAll);
	break;
      }
      break;
    case FeatureInfo::boolean:
      if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rNO,
					SdParam::reservedName + Sd::rYES),
			parm))
	return 0;
      switch (features[i].name) {
#if 0
      case Sd::rDATATAG:
	if (parm.type == SdParam::reservedName + Sd::rYES)
	  message(ParserMessages::datatagNotImplemented);
	break;
#endif
      case Sd::rEMPTYNRM:
	if (parm.type == SdParam::reservedName + Sd::rNO
	    && sdBuilder.sd->startTagNetEnable() == Sd::netEnableImmednet) {
	  message(ParserMessages::immednetRequiresEmptynrm);
	  sdBuilder.valid = 0;
	}
	break;
      }
      sdBuilder.sd->setBooleanFeature(Sd::BooleanFeature(booleanFeature++),
				      parm.type == (SdParam::reservedName
						    + Sd::rYES));
      break;
    }
  }
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rNOASSERT,
                                    SdParam::reservedName + Sd::rTYPE),
		    parm))
    return 0;
  switch (parm.type) {
  case SdParam::reservedName + Sd::rNOASSERT:
    sdBuilder.sd->setTypeValid(0);
    break;
  case SdParam::reservedName + Sd::rTYPE:
    sdBuilder.sd->setTypeValid(1);
    break;
  }
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rENTITIES), parm))
    return 0;
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rNOASSERT,
                                    SdParam::reservedName + Sd::rREF),
		    parm))
    return 0;
  if (parm.type == SdParam::reservedName + Sd::rNOASSERT) {
    sdBuilder.sd->setIntegrallyStored(0);
    sdBuilder.sd->setEntityRef(Sd::entityRefAny);
  }
  else {
    if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rNONE,
                                      SdParam::reservedName + Sd::rINTERNAL,
                                      SdParam::reservedName + Sd::rANY),
		      parm))
      return 0;
    switch (parm.type) {
    case SdParam::reservedName + Sd::rNONE:
      sdBuilder.sd->setEntityRef(Sd::entityRefNone);
      break;
    case SdParam::reservedName + Sd::rINTERNAL:
      sdBuilder.sd->setEntityRef(Sd::entityRefInternal);
      break;
    case SdParam::reservedName + Sd::rANY:
      sdBuilder.sd->setEntityRef(Sd::entityRefAny);
      break;
    }
    if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rINTEGRAL), parm))
      return 0;
    if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rNO,
	                              SdParam::reservedName + Sd::rYES),
		      parm))
      return 0;
    sdBuilder.sd->setIntegrallyStored(parm.type == (SdParam::reservedName + Sd::rYES));
  }
  return parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rAPPINFO),
		      parm);
}

Boolean Parser::sdParseAppinfo(SdBuilder &, SdParam &parm)
{
  Location location(currentLocation());
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rNONE,
				    SdParam::minimumLiteral),
		    parm))
    return 0;
  AppinfoEvent *event;
  if (parm.type == SdParam::minimumLiteral)
    event = new (eventAllocator()) AppinfoEvent(parm.literalText, location);
  else
    event = new (eventAllocator()) AppinfoEvent(location);
  eventHandler().appinfo(event);
  return 1;
}

Boolean Parser::sdParseSeealso(SdBuilder &sdBuilder, SdParam &parm)
{
  SdParam::Type final = sdBuilder.external ? SdParam::eE : SdParam::mdc;
  if (!parseSdParam(AllowedSdParams(SdParam::reservedName + Sd::rSEEALSO, final), parm))
    return 0;
  if (parm.type == final)
    return 1;
  requireWWW(sdBuilder);
  if (!parseSdParam(AllowedSdParams(SdParam::minimumLiteral,
				    SdParam::reservedName + Sd::rNONE), parm))
    return 0;
  if (parm.type == SdParam::reservedName + Sd::rNONE)
    return parseSdParam(AllowedSdParams(final), parm);
  do {
    if (!parseSdParam(AllowedSdParams(SdParam::minimumLiteral, final), parm))
      return 0;
  } while (parm.type != final);
  return 1;
}

Boolean Parser::translateSyntax(CharSwitcher &switcher,
				const CharsetInfo &syntaxCharset,
				const CharsetInfo &internalCharset,
				WideChar syntaxChar,
				Char &docChar)
{
  syntaxChar = switcher.subst(syntaxChar);
  UnivChar univChar;
  if (syntaxCharset.descToUniv(syntaxChar, univChar)
      && univToDescCheck(internalCharset, univChar, docChar))
    return 1;
  message(sd().internalCharsetIsDocCharset()
          ? ParserMessages::translateSyntaxCharDoc
	  : ParserMessages::translateSyntaxCharInternal,
	  NumberMessageArg(syntaxChar));
  return 0;
}

void Parser::translateRange(SdBuilder &sdBuilder, SyntaxChar start,
			    SyntaxChar end, ISet<Char> &chars)
{
#if 0
  do {
    Char docChar;
    if (!translateSyntax(sdBuilder, start, docChar))
      break;
    chars.add(docChar);
  } while (start++ != end);
#endif
  for (;;) {
    SyntaxChar doneUpTo = end;
    Boolean gotSwitch = 0;
    WideChar firstSwitch;
    for (size_t i = 0; i < sdBuilder.switcher.nSwitches(); i++) {
      WideChar c = sdBuilder.switcher.switchFrom(i);
      if (start <= c && c <= end) {
	if (!gotSwitch) {
	  gotSwitch = 1;
	  firstSwitch = c;
	}
	else if (c < firstSwitch)
	  firstSwitch = c;
      }
    }
    if (gotSwitch && firstSwitch == start) {
      doneUpTo = start;
      Char docChar;
      if (translateSyntax(sdBuilder, start, docChar))
	chars.add(docChar);
    }
    else {
      if (gotSwitch)
	doneUpTo = firstSwitch - 1;
      Char docChar;
      Number count;
      if (translateSyntaxNoSwitch(sdBuilder, start, docChar, count)) {
	if (count - 1 < doneUpTo - start)
	  doneUpTo = start + (count - 1);
	chars.addRange(docChar, docChar + (doneUpTo - start));
      }
    }
    if (doneUpTo == end)
      break;
    start = doneUpTo + 1;
  }
}

Boolean Parser::translateSyntax(SdBuilder &sdBuilder,
				WideChar syntaxChar, Char &docChar)
{
  Number count;
  return translateSyntaxNoSwitch(sdBuilder,
				 sdBuilder.switcher.subst(syntaxChar),
				 docChar,
				 count);
}

Boolean Parser::translateSyntaxNoSwitch(SdBuilder &sdBuilder,
					WideChar syntaxChar, Char &docChar,
					Number &count)
{
  Number n;
  StringC str;
  CharsetDeclRange::Type type;
  const PublicId *id;
  if (sdBuilder.sd->internalCharsetIsDocCharset()
      && sdBuilder.syntaxCharsetDecl.getCharInfo(syntaxChar,
					         id,
					         type,
					         n,
					         str,
					         count)) {
    ISet<WideChar> docChars;
    switch (type) {
    case CharsetDeclRange::unused:
      break;
    case CharsetDeclRange::string:
      sdBuilder.sd->docCharsetDecl().stringToChar(str, docChars);
      break;
    case CharsetDeclRange::number:
      {
	Number count2;
	sdBuilder.sd->docCharsetDecl().numberToChar(id, n, docChars, count2);
	if (!docChars.isEmpty() && count2 < count)
	  count = count2;
      }
      break;
    default:
      CANNOT_HAPPEN();
    }
    if (!docChars.isEmpty()) {
      if (!docChars.isSingleton() && options().warnSgmlDecl)
	message(ParserMessages::ambiguousDocCharacter,
		CharsetMessageArg(docChars));
      ISetIter<WideChar> iter(docChars);
      WideChar min, max;
      if (iter.next(min, max) && min <= charMax) {
	docChar = Char(min);
	return 1;
      }
    }
  }
  UnivChar univChar;
  WideChar alsoMax, count2;
  if (sdBuilder.syntaxCharset.descToUniv(syntaxChar, univChar, alsoMax)
      && univToDescCheck(sdBuilder.sd->internalCharset(), univChar, docChar,
			 count2)) {
    count = (alsoMax - syntaxChar) + 1;
    if (count2 < count)
      count = count2;
    return 1;
  }
  sdBuilder.valid = 0;
  message(sd().internalCharsetIsDocCharset()
          ? ParserMessages::translateSyntaxCharDoc
	  : ParserMessages::translateSyntaxCharInternal,
	  NumberMessageArg(syntaxChar));
  return 0;
}


Boolean Parser::translateSyntax(SdBuilder &sdBuilder,
				const String<SyntaxChar> &syntaxString,
				StringC &docString)
{
  docString.resize(0);
  int ret = 1;
  for (size_t i = 0; i < syntaxString.size(); i++) {
    Char c;
    if (translateSyntax(sdBuilder, syntaxString[i], c))
      docString += c;
    else
      ret = 0;
  }
  return ret;
}

Boolean Parser::translateName(SdBuilder &sdBuilder,
			      const StringC &name,
			      StringC &str)
{
  str.resize(name.size());
  for (size_t i = 0; i < name.size(); i++) {
    UnivChar univChar;
    Boolean ret = sd().internalCharset().descToUniv(name[i], univChar);
    // Might switch hyphen or period.
    univChar = translateUniv(univChar, sdBuilder.switcher,
			     sdBuilder.syntaxCharset);
    ASSERT(ret != 0);
    if (!univToDescCheck(sdBuilder.sd->internalCharset(), univChar, str[i])) {
      message(ParserMessages::translateDocChar, NumberMessageArg(univChar));
      sdBuilder.valid = 0;
      return 0;
    }
  }
  return 1;
}

UnivChar Parser::translateUniv(UnivChar univChar,
			       CharSwitcher &switcher,
			       const CharsetInfo &syntaxCharset)
{
  WideChar syntaxChar;
  ISet<WideChar> syntaxChars;
  if (syntaxCharset.univToDesc(univChar, syntaxChar, syntaxChars) != 1) {
    message(ParserMessages::missingSyntaxChar,
	    NumberMessageArg(univChar));
    return univChar;
  }
  SyntaxChar tem = switcher.subst(syntaxChar);
  if (tem != syntaxChar && !syntaxCharset.descToUniv(tem, univChar))
    message(sd().internalCharsetIsDocCharset()
            ? ParserMessages::translateSyntaxCharDoc
            : ParserMessages::translateSyntaxCharInternal,
	    NumberMessageArg(tem));
  return univChar;
}

Boolean Parser::checkNotFunction(const Syntax &syn, Char c)
{
  if (syn.charSet(Syntax::functionChar)->contains(c)) {
    message(ParserMessages::oneFunction, NumberMessageArg(c));
    return 0;
  }
  else
    return 1;
}


// Check that it has at most one B sequence and that it
// is not adjacent to a blank sequence.

Boolean Parser::checkShortrefDelim(const Syntax &syn,
				   const CharsetInfo &charset,
				   const StringC &delim)
{
  Boolean hadB = 0;
  Char letterB = charset.execToDesc('B');
  const ISet<Char> *bSet = syn.charSet(Syntax::blank);
  for (size_t i = 0; i < delim.size(); i++)
    if (delim[i] == letterB) {
      if (hadB) {
	message(ParserMessages::multipleBSequence, StringMessageArg(delim));
	return 0;
      }
      hadB = 1;
      if (i > 0 && bSet->contains(delim[i - 1])) {
	message(ParserMessages::blankAdjacentBSequence,
		StringMessageArg(delim));
	return 0;
      }
      while (i + 1 < delim.size() && delim[i + 1] == letterB)
	i++;
      if (i < delim.size() - 1 && bSet->contains(delim[i + 1])) {
	message(ParserMessages::blankAdjacentBSequence,
		StringMessageArg(delim));
	return 0;
      }
    }
  return 1;
}

Boolean Parser::checkGeneralDelim(const Syntax &syn, const StringC &delim)
{
  const ISet<Char> *functionSet = syn.charSet(Syntax::functionChar);
  if (delim.size() > 0) {
    Boolean allFunction = 1;
    for (size_t i = 0; i < delim.size(); i++)
      if (!functionSet->contains(delim[i]))
	allFunction = 0;
    if (allFunction) {
      message(ParserMessages::generalDelimAllFunction,
	      StringMessageArg(delim));
      return 0;
    }
  }
  return 1;
}

Boolean Parser::checkSwitches(CharSwitcher &switcher,
			      const CharsetInfo &syntaxCharset)
{
  Boolean valid = 1;
  for (size_t i = 0; i < switcher.nSwitches(); i++) {
    WideChar c[2];
    c[0] = switcher.switchFrom(i);
    c[1] = switcher.switchTo(i);
    for (int j = 0; j < 2; j++) {
      UnivChar univChar;
      if (syntaxCharset.descToUniv(c[j], univChar)) {
	// Check that it is not Digit Lcletter or Ucletter
	if ((UnivCharsetDesc::a <= univChar
	     && univChar < UnivCharsetDesc::a + 26)
	    || (UnivCharsetDesc::A <= univChar
		&& univChar < UnivCharsetDesc::A + 26)
	    || (UnivCharsetDesc::zero <= univChar
		&& univChar < UnivCharsetDesc::zero + 10)) {
	  message(ParserMessages::switchLetterDigit,
		  NumberMessageArg(univChar));
	  valid = 0;
	}
      }
    }
  }
  return valid;
}

Boolean Parser::checkSwitchesMarkup(CharSwitcher &switcher)
{
  Boolean valid = 1;
  size_t nSwitches = switcher.nSwitches();
  for (size_t i = 0; i < nSwitches; i++)
    if (!switcher.switchUsed(i)) {
      // If the switch wasn't used,
      // then the character wasn't a markup character.
      message(ParserMessages::switchNotMarkup,
	      NumberMessageArg(switcher.switchFrom(i)));
      valid = 0;
    }
  return valid;
}

void Parser::checkSyntaxNamelen(const Syntax &syn)
{
  size_t namelen = syn.namelen();
  int i;
  for (i = 0; i < Syntax::nDelimGeneral; i++)
    if (syn.delimGeneral(i).size() > namelen)
      message(ParserMessages::delimiterLength,
	      StringMessageArg(syn.delimGeneral(i)),
	      NumberMessageArg(namelen));
  for (i = 0; i < syn.nDelimShortrefComplex(); i++)
    if (syn.delimShortrefComplex(i).size() > namelen)
      message(ParserMessages::delimiterLength,
	      StringMessageArg(syn.delimShortrefComplex(i)),
	      NumberMessageArg(namelen));
  for (i = 0; i < Syntax::nNames; i++)
    if (syn.reservedName(Syntax::ReservedName(i)).size() > namelen
	&& options().warnSgmlDecl)
      message(ParserMessages::reservedNameLength,
	      StringMessageArg(syn.reservedName(Syntax::ReservedName(i))),
	      NumberMessageArg(namelen));
}

Boolean Parser::univToDescCheck(const CharsetInfo &charset, UnivChar from,
				Char &to)
{
  WideChar c;
  ISet<WideChar> descSet;
  unsigned ret = charset.univToDesc(from, c, descSet);
  if (ret > 1) {
    if (options().warnSgmlDecl)
      message(ParserMessages::ambiguousDocCharacter,
	      CharsetMessageArg(descSet));
    ret = 1;
  }
  if (ret && c <= charMax) {
    to = Char(c);
    return 1;
  }
  return 0;
}

Boolean Parser::univToDescCheck(const CharsetInfo &charset, UnivChar from,
				Char &to, WideChar &count)
{
  WideChar c;
  ISet<WideChar> descSet;
  unsigned ret = charset.univToDesc(from, c, descSet, count);
  if (ret > 1) {
    if (options().warnSgmlDecl)
      message(ParserMessages::ambiguousDocCharacter,
	      CharsetMessageArg(descSet));
    ret = 1;
  }
  if (ret && c <= charMax) {
    to = Char(c);
    return 1;
  }
  return 0;
}

Boolean Parser::parseSdParam(const AllowedSdParams &allow,
			     SdParam &parm)
{
  for (;;) {
    Token token = getToken(sdMode);
    switch (token) {
    case tokenUnrecognized:
      if (reportNonSgmlCharacter())
	break;
      {
	message(ParserMessages::markupDeclarationCharacter,
		StringMessageArg(currentToken()),
		AllowedSdParamsMessageArg(allow, sdPointer()));
      }
      return 0;
    case tokenEe:
      if (allow.param(SdParam::eE)) {
	parm.type = SdParam::eE;
	if (currentMarkup())
	  currentMarkup()->addEntityEnd();
	popInputStack();
	return 1;
      }
      message(ParserMessages::sdEntityEnd,
	      AllowedSdParamsMessageArg(allow, sdPointer()));
      return 0;
    case tokenS:
      if (currentMarkup())
	currentMarkup()->addS(currentChar());
      break;
    case tokenCom:
      if (!parseComment(sdcomMode))
	return 0;
      break;
    case tokenDso:
    case tokenGrpo:
    case tokenMinusGrpo:
    case tokenPlusGrpo:
    case tokenRni:
    case tokenPeroNameStart:
    case tokenPeroGrpo:
      sdParamInvalidToken(token, allow);
      return 0;
    case tokenMinus:
      if (allow.param(SdParam::minus)) {
	parm.type = SdParam::minus;
	return 1;
      }
      sdParamInvalidToken(tokenMinus, allow);
      return 0;
    case tokenLita:
    case tokenLit:
      {
	Boolean lita = (token == tokenLita);
	if (allow.param(SdParam::minimumLiteral)) {
	  if (!parseMinimumLiteral(lita, parm.literalText))
	    return 0;
	  parm.type = SdParam::minimumLiteral;
	  if (currentMarkup())
	    currentMarkup()->addLiteral(parm.literalText);
	}
	else if (allow.param(SdParam::paramLiteral)) {
	  if (!parseSdParamLiteral(lita, parm.paramLiteralText))
	    return 0;
	  parm.type = SdParam::paramLiteral;
	}
	else if (allow.param(SdParam::systemIdentifier)) {
	  if (!parseSdSystemIdentifier(lita, parm.literalText))
	    return 0;
	  parm.type = SdParam::systemIdentifier;
	}
	else {
	  sdParamInvalidToken(token, allow);
	  return 0;
	}
	return 1;
      }
    case tokenMdc:
      if (allow.param(SdParam::mdc)) {
	parm.type = SdParam::mdc;
	if (currentMarkup())
	  currentMarkup()->addDelim(Syntax::dMDC);
	return 1;
      }
      sdParamInvalidToken(tokenMdc, allow);
      return 0;
    case tokenNameStart:
      {
	extendNameToken(syntax().namelen(), ParserMessages::nameLength);
	getCurrentToken(syntax().generalSubstTable(), parm.token);
	if (allow.param(SdParam::capacityName)) {
	  if (sd().lookupCapacityName(parm.token, parm.capacityIndex)) {
	    parm.type = SdParam::capacityName;
	    if (currentMarkup())
	      currentMarkup()->addName(currentInput());
	    return 1;
	  }
	}
	if (allow.param(SdParam::referenceReservedName)) {
	  if (syntax().lookupReservedName(parm.token,
					  &parm.reservedNameIndex)) {
	    parm.type = SdParam::referenceReservedName;
	    if (currentMarkup())
	      currentMarkup()->addName(currentInput());
	    return 1;
	  }
	}
	if (allow.param(SdParam::generalDelimiterName)) {
	  if (sd().lookupGeneralDelimiterName(parm.token,
					      parm.delimGeneralIndex)) {
	    parm.type = SdParam::generalDelimiterName;
	    if (currentMarkup())
	      currentMarkup()->addName(currentInput());
	    return 1;
	  }
	}
	if (allow.param(SdParam::quantityName)) {
	  if (sd().lookupQuantityName(parm.token, parm.quantityIndex)) {
	    parm.type = SdParam::quantityName;
	    if (currentMarkup())
	      currentMarkup()->addName(currentInput());
	    return 1;
	  }
	}
	for (int i = 0;; i++) {
	  SdParam::Type t = allow.get(i);
	  if (t == SdParam::invalid)
	    break;
	  if (t >= SdParam::reservedName) {
	    Sd::ReservedName sdReservedName
	      = Sd::ReservedName(t - SdParam::reservedName);
	    if (parm.token == sd().reservedName(sdReservedName)) {
	      parm.type = t;
	      if (currentMarkup())
		currentMarkup()->addSdReservedName(sdReservedName,
						  currentInput());
	      return 1;
	    }
	  }
	}
	if (allow.param(SdParam::name)) {
	  parm.type = SdParam::name;
	  if (currentMarkup())
	    currentMarkup()->addName(currentInput());
	  return 1;
	}
	{
	  message(ParserMessages::sdInvalidNameToken,
		  StringMessageArg(parm.token),
		  AllowedSdParamsMessageArg(allow, sdPointer()));
	}
	return 0;
      }
    case tokenDigit:
      if (allow.param(SdParam::number)) {
	extendNumber(syntax().namelen(), ParserMessages::numberLength);
	parm.type = SdParam::number;
	unsigned long n;
	if (!stringToNumber(currentInput()->currentTokenStart(),
			    currentInput()->currentTokenLength(),
			    n)
	    || n > Number(-1)) {
	  message(ParserMessages::numberTooBig,
		  StringMessageArg(currentToken()));
	  parm.n = Number(-1);
	}
	else {
	  if (currentMarkup())
	    currentMarkup()->addNumber(currentInput());
	  parm.n = Number(n);
	}
	Token token = getToken(sdMode);
	if (token == tokenNameStart)
	  message(ParserMessages::psRequired);
	currentInput()->ungetToken();
	return 1;
      }
      sdParamInvalidToken(tokenDigit, allow);
      return 0;
    default:
      CANNOT_HAPPEN();
    }
  }
}

// This is a separate function, because we might want SyntaxChar
// to be bigger than Char.

Boolean Parser::parseSdParamLiteral(Boolean lita, String<SyntaxChar> &str)
{
  Location loc(currentLocation());
  loc += 1;
  SdText text(loc, lita);	// first character of content
  str.resize(0);
  const unsigned refLitlen = Syntax::referenceQuantity(Syntax::qLITLEN);

  Mode mode = lita ? sdplitaMode : sdplitMode;
  for (;;) {
    Token token = getToken(mode);
    switch (token) {
    case tokenEe:
      message(ParserMessages::literalLevel);
      return 0;
    case tokenUnrecognized:
      if (reportNonSgmlCharacter())
	break;
      if (options().errorSignificant)
	message(ParserMessages::sdLiteralSignificant,
		StringMessageArg(currentToken()));
      text.addChar(currentChar(), currentLocation());
      break;
    case tokenCroDigit:
      {
	InputSource *in = currentInput();
	Location startLocation = currentLocation();
	in->discardInitial();
	extendNumber(syntax().namelen(), ParserMessages::numberLength);
	unsigned long n;
	Boolean valid;
	if (!stringToNumber(in->currentTokenStart(),
			    in->currentTokenLength(),
			    n)
	    || n > syntaxCharMax) {
	  message(ParserMessages::syntaxCharacterNumber,
		  StringMessageArg(currentToken()));
	  valid = 0;
	}
	else
	  valid = 1;
	Owner<Markup> markupPtr;
	if (eventsWanted().wantPrologMarkup()) {
	  markupPtr = new Markup;
	  markupPtr->addDelim(Syntax::dCRO);
	  markupPtr->addNumber(in);
	  switch (getToken(refMode)) {
	  case tokenRefc:
	    markupPtr->addDelim(Syntax::dREFC);
	    break;
	  case tokenRe:
	    markupPtr->addRefEndRe();
	    if (options().warnRefc)
	      message(ParserMessages::refc);
	    break;
	  default:
	    if (options().warnRefc)
	      message(ParserMessages::refc);
	    break;
	  }
	}
	else if (options().warnRefc) {
	  if (getToken(refMode) != tokenRefc)
	    message(ParserMessages::refc);
	}
	else
	  (void)getToken(refMode);
	if (valid)
	  text.addChar(SyntaxChar(n),
		       Location(new NumericCharRefOrigin(startLocation,
							 currentLocation().index()
							 + currentInput()->currentTokenLength()
							 - startLocation.index(),
							 markupPtr),
				0));
      }
      break;
    case tokenCroNameStart:
      if (!parseNamedCharRef())
	return 0;
      break;
    case tokenLit:
    case tokenLita:
      goto done;
    case tokenPeroNameStart:
    case tokenPeroGrpo:
      message(ParserMessages::sdParameterEntity);
      {
	Location loc(currentLocation());
	const Char *p = currentInput()->currentTokenStart();
	for (size_t count = currentInput()->currentTokenLength();
	     count > 0;
	     count--) {
	  text.addChar(*p++, loc);
	  loc += 1;
	}
      }
      break;
    case tokenChar:
      if (text.string().size() > refLitlen
	  && currentChar() == syntax().standardFunction(Syntax::fRE)) {
	message(ParserMessages::parameterLiteralLength, NumberMessageArg(refLitlen));
	// guess that the closing delimiter has been omitted
	message(ParserMessages::literalClosingDelimiter);
	return 0;
      }
      text.addChar(currentChar(), currentLocation());
      break;
    }
  }
 done:
  if (text.string().size() > refLitlen)
    message(ParserMessages::parameterLiteralLength,
	    NumberMessageArg(refLitlen));
  
  str = text.string();
  if (currentMarkup())
    currentMarkup()->addSdLiteral(text);
  return 1;
}

Boolean Parser::parseSdSystemIdentifier(Boolean lita, Text &text)
{
  text.addStartDelim(currentLocation());

  const unsigned refLitlen = Syntax::referenceQuantity(Syntax::qLITLEN);

  Mode mode = lita ? sdslitaMode : sdslitMode;
  for (;;) {
    Token token = getToken(mode);
    switch (token) {
    case tokenEe:
      message(ParserMessages::literalLevel);
      return 0;
    case tokenUnrecognized:
      if (reportNonSgmlCharacter())
	break;
      if (options().errorSignificant)
	message(ParserMessages::sdLiteralSignificant,
		StringMessageArg(currentToken()));
      text.addChar(currentChar(), currentLocation());
      break;
    case tokenLit:
    case tokenLita:
      text.addEndDelim(currentLocation(), token == tokenLita);
      goto done;
    case tokenChar:
      text.addChar(currentChar(), currentLocation());
      break;
    default:
      CANNOT_HAPPEN();
    }
  }
done:
  if (text.string().size() > refLitlen)
    message(ParserMessages::systemIdentifierLength,
	    NumberMessageArg(refLitlen));
  if (currentMarkup())
    currentMarkup()->addLiteral(text);
  return 1;
}

Boolean Parser::stringToNumber(const Char *s, size_t length,
			       unsigned long &result)
{
  unsigned long n = 0;
  for (; length > 0; length--, s++) {
    int val = sd().digitWeight(*s);
    if (n <= ULONG_MAX/10 && (n *= 10) <= ULONG_MAX - val)
      n += val;
    else
      return 0;
  }
  result = n;
  return 1;
}

void Parser::sdParamInvalidToken(Token token,
				 const AllowedSdParams &allow)
{
  message(ParserMessages::sdParamInvalidToken,
	  TokenMessageArg(token, sdMode, syntaxPointer(), sdPointer()),
	  AllowedSdParamsMessageArg(allow, sdPointer()));
}

void Parser::sdParamConvertToLiteral(SdParam &parm)
{
  if (parm.type == SdParam::number) {
    parm.type = SdParam::paramLiteral;
    parm.paramLiteralText.resize(1);
    parm.paramLiteralText[0] = parm.n;
  }
}

void Parser::requireWWW(SdBuilder &sdBuilder)
{
  if (!sdBuilder.www) {
    message(ParserMessages::wwwRequired);
    sdBuilder.www = 1;
  }
}

AllowedSdParams::AllowedSdParams(SdParam::Type arg1, SdParam::Type arg2,
				 SdParam::Type arg3, SdParam::Type arg4,
				 SdParam::Type arg5, SdParam::Type arg6)
{
  allow_[0] = arg1;
  allow_[1] = arg2;
  allow_[2] = arg3;
  allow_[3] = arg4;
  allow_[4] = arg5;
  allow_[5] = arg6;
}

Boolean AllowedSdParams::param(SdParam::Type t) const
{
  for (int i = 0; i < maxAllow && allow_[i] != SdParam::invalid; i++)
    if (t == allow_[i])
      return 1;
  return 0;
}

SdParam::Type AllowedSdParams::get(int i) const
{
  return i < 0 || i >= maxAllow ? SdParam::Type(SdParam::invalid) : allow_[i];
}

AllowedSdParamsMessageArg::AllowedSdParamsMessageArg(
  const AllowedSdParams &allow,
  const ConstPtr<Sd> &sd)
: allow_(allow), sd_(sd)
{
}

MessageArg *AllowedSdParamsMessageArg::copy() const
{
  return new AllowedSdParamsMessageArg(*this);
}

void AllowedSdParamsMessageArg::append(MessageBuilder &builder) const
{
  for (int i = 0;; i++) {
    SdParam::Type type = allow_.get(i);
    if (type == SdParam::invalid)
      break;
    if (i != 0)
      builder.appendFragment(ParserMessages::listSep);
    switch (type) {
    case SdParam::eE:
      builder.appendFragment(ParserMessages::entityEnd);
      break;
    case SdParam::minimumLiteral:
      builder.appendFragment(ParserMessages::minimumLiteral);
      break;
    case SdParam::mdc:
      {
	builder.appendFragment(ParserMessages::delimStart);
	Char c = sd_->execToInternal('>');
	builder.appendChars(&c, 1);
	builder.appendFragment(ParserMessages::delimEnd);
      }
      break;
    case SdParam::number:
      builder.appendFragment(ParserMessages::number);
      break;
    case SdParam::name:
      builder.appendFragment(ParserMessages::name);
      break;
    case SdParam::paramLiteral:
      builder.appendFragment(ParserMessages::parameterLiteral);
      break;
    case SdParam::systemIdentifier:
      builder.appendFragment(ParserMessages::systemIdentifier);
      break;
    case SdParam::capacityName:
      builder.appendFragment(ParserMessages::capacityName);
      break;
    case SdParam::generalDelimiterName:
      builder.appendFragment(ParserMessages::generalDelimiteRoleName);
      break;
    case SdParam::referenceReservedName:
      builder.appendFragment(ParserMessages::referenceReservedName);
      break;
    case SdParam::quantityName:
      builder.appendFragment(ParserMessages::quantityName);
      break;
    case SdParam::minus:
      {
	StringC str(sd_->execToInternal("..."));
	builder.appendChars(str.data(), str.size());
	break;
      }
    default:
      {
	StringC str(sd_->reservedName(type - SdParam::reservedName));
	builder.appendChars(str.data(), str.size());
	break;
      }
    }
  }
}

SdBuilder::SdBuilder()
: valid(1), externalSyntax(0), enr(0), www(0), external(0)
{
}

void SdBuilder::addFormalError(const Location &location,
			       const MessageType1 &message,
			       const StringC &id)
{
  formalErrorList.insert(new SdFormalError(location, message, id));
}

SdFormalError::SdFormalError(const Location &location,
			     const MessageType1 &message,
			     const StringC &id)
: location_(location),
  message_(&message),
  id_(id)
{
}

void SdFormalError::send(ParserState &parser)
{
  parser.Messenger::setNextLocation(location_);
  parser.message(*message_, StringMessageArg(id_));
}

CharSwitcher::CharSwitcher()
{
}

void CharSwitcher::addSwitch(WideChar from, WideChar to)
{
  switches_.push_back(from);
  switches_.push_back(to);
  switchUsed_.push_back(0);
}

SyntaxChar CharSwitcher::subst(WideChar c)
{
  for (size_t i = 0; i < switches_.size(); i += 2)
    if (switches_[i] == c) {
      switchUsed_[i/2] = 1;
      return switches_[i + 1];
    }
  return c;
}

size_t CharSwitcher::nSwitches() const
{
  return switchUsed_.size();
}

Boolean CharSwitcher::switchUsed(size_t i) const
{
  return switchUsed_[i];
}

WideChar CharSwitcher::switchFrom(size_t i) const
{
  return switches_[i*2];
}

WideChar CharSwitcher::switchTo(size_t i) const
{
  return switches_[i*2 + 1];
}

CharsetMessageArg::CharsetMessageArg(const ISet<WideChar> &set)
: set_(set)
{
}

MessageArg *CharsetMessageArg::copy() const
{
  return new CharsetMessageArg(*this);
}

void CharsetMessageArg::append(MessageBuilder &builder) const
{
  ISetIter<WideChar> iter(set_);
  WideChar min, max;
  Boolean first = 1;
  while (iter.next(min, max)) {
    if (first)
      first = 0;
    else
      builder.appendFragment(ParserMessages::listSep);
    builder.appendNumber(min);
    if (max != min) {
      builder.appendFragment(max == min + 1
			     ? ParserMessages::listSep
			     : ParserMessages::rangeSep);
      builder.appendNumber(max);
    }
  }
}

#ifdef SP_NAMESPACE
}
#endif
