/* National Institute of Standards and Technology (NIST)
/* National Computer System Laboratory (NCSL)
/* Office Systems Engineering (OSE) Group
/* ********************************************************************
/*                            D I S C L A I M E R
/*                              (March 8, 1989)
/*  
/* There is no warranty for the NIST NCSL OSE SGML parser and/or the NIST
/* NCSL OSE SGML parser validation suite.  If the SGML parser and/or
/* validation suite is modified by someone else and passed on, NIST wants
/* the parser's recipients to know that what they have is not what NIST
/* distributed, so that any problems introduced by others will not
/* reflect on our reputation.
/* 
/* Policies
/* 
/* 1. Anyone may copy and distribute verbatim copies of the SGML source
/* code as received in any medium.
/* 
/* 2. Anyone may modify your copy or copies of SGML parser source code or
/* any portion of it, and copy and distribute such modifications provided
/* that all modifications are clearly associated with the entity that
/* performs the modifications.
/* 
/* NO WARRANTY
/* ===========
/* 
/* NIST PROVIDES ABSOLUTELY NO WARRANTY.  THE SGML PARSER AND VALIDATION
/* SUITE ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
/* EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
/* THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS
/* WITH YOU.  SHOULD THE SGML PARSER OR VALIDATION SUITE PROVE DEFECTIVE,
/* YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
/* 
/* IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL NIST BE LIABLE FOR
/* DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER SPECIAL,
/* INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
/* INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
/* BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A
/* FAILURE OF THE PROGRAM TO OPERATE WITH PROGRAMS NOT DISTRIBUTED BY
/* NIST) THE PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF
/* SUCH DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
*/

/************************************************************************/
/*   TITLE:          SGML PARSER                                        */
/*   SYSTEM:         DTD PROCESSOR                                      */
/*   SUBSYSTEM:                                                         */
/*   SOURCE FILE:    DTDELT.C                                           */
/*   AUTHOR:         Jim Heath                                          */
/*                                                                      */
/*   DATE CREATED:                                                      */
/*   LAST MODIFIED:                                                     */
/*                                                                      */
/*                  REVISIONS                                           */
/*   WHEN      WHO            WHY                                       */
/************************************************************************/
#include <stdio.h>
#include <setjmp.h>
#include <string.h>
#include <ctype.h>
#include "qntyset.h"
#include "dtd.h"
#include "dtdfncs.h"
#include "dtdglbl.h"

extern int debug;
char *rec;

static char savebuff[1024], *savebuffptr = savebuff;
static int saveinfo = OFF, savebufflen = 0, miscflag = 0;
static int eltindex = 0;
static char eltnames[MAXELTS * (NAMELEN + 1)];

/* ============================================================ */
/* We have just finished reading <!ELEMENT from the input stream
*/
int doelement()
{

   FUNCTRACE("doelt1");
   /*   memset(eltnames, '\0', sizeof(eltnames));
   eltindex = 0;*/
   saveinfo = OFF;
   if (INPPS() == EOF)
      terminate(1, "EOF in ELEMENT declaration");
   ADDCHAR(SPACE);
   if (dosingleelt())
      return(1);
   return(doeltgroup());
}
/* ============================================================ */
int dosingleelt()
{
   char namearray[NAMELEN + 1], temp[128], *mydecl;
   char rankstem[NAMELEN + 1], *stemptr = rankstem;
   int j;

   if (geteltname(namearray) == GOOD) {
      ADDSTRING(namearray);
      if ((j = INPPS()) == EOF)
         terminate(1, "EOF in element declaration");
      j = jgetc();
      if (ISDIGIT(j)) {
         jungetc(j);
         ADDCHAR(SPACE);
         if ((j = INPNBR(&stemptr, NAMELEN - 1, noxlat)) == EOF)
            terminate(1, "EOF in element declaration");
         ADDSTRING(rankstem);
         if (j != GOOD)
            syntxerr("syntax error in rank suffix");
         if ((strlen(namearray) + strlen(rankstem)) > NAMELEN)
            syntxerr("combined rank stem + rank suffix > NAMELEN");
         strcat(namearray, rankstem);
      }
      else jungetc(j);

      if (eltindex >= MAXELTS)
         terminate(1, "MAX # of elements has been exceeded");
      if (ckfordup(eltindex++, eltnames, namearray) == TRUE){
         GETDECLADDR(&mydecl);
         printf("%s\n", mydecl);
         strcpy(temp, "Multiple element declarations exist for:  ");
         strcat(temp, namearray);
         terminate(1, temp);
      }
      putsymtbl(namearray);
      doelt2(namearray, 1);
      return(TRUE);
   }
   return(FALSE);
}
/* ============================================================ */
doeltgroup()
{
   int eltcount;
   REGISTER int namecount, j;
   char *mydecl, temp[128], rankstem[NAMELEN + 1], *stemptr = rankstem;
   char tempnames[GRPCNT * (NAMELEN + 1)];

   eltcount = namecount = getnamegrp(tempnames, ISALPHA, isnmchar);
   memset(rankstem, '\0', sizeof(rankstem));
   if ((j = INPPS()) == EOF)
      terminate(1, "EOF in element declaration");
   j = jgetc();
   if (ISDIGIT(j)) {
      jungetc(j);
      ADDCHAR(SPACE);
      if ((j = INPNBR(&stemptr, NAMELEN - 1, noxlat)) == EOF)
         terminate(1, "EOF in element declaration");
      ADDSTRING(rankstem);
      if (j != GOOD)
         syntxerr("syntax error in rank suffix");
   }
   else jungetc(j);

   if (eltindex >= MAXELTS)
      terminate(1, "MAX # of elements has been exceeded");
   if ((strlen(tempnames) + strlen(rankstem)) > NAMELEN)
      syntxerr("combined rank stem + rank suffix > NAMELEN");
   strcat(tempnames, rankstem);

   if (ckfordup(eltindex++, eltnames, tempnames) == FALSE)
      putsymtbl(tempnames);
   else {
      GETDECLADDR(&mydecl);
      printf("%s\n", mydecl);
      strcpy(temp, "Multiple element declarations exist for:  ");
      strcat(temp, tempnames);
      terminate(1, temp);
   }
   doeltgroup2(namecount, eltcount, tempnames, rankstem, mydecl);
   return(eltcount);
}
/* ============================================================ */
doeltgroup2(namecount, eltcount, tempnames, rankstem, mydecl)
int namecount, eltcount;
char tempnames[], rankstem[], *mydecl;
{
   char temp[128], *nmptr = tempnames;

   memset(savebuff, '\0', sizeof(savebuff));
   savebuffptr = savebuff;
   savebufflen = 0;
   saveinfo = ON;
   doelt2(tempnames, eltcount);
   nmptr += (NAMELEN + 1);
   namecount--;
   while(namecount--){
      if (eltindex >= MAXELTS)
         terminate(1, "MAX # of elements has been exceeded");
      if ((strlen(nmptr) + strlen(rankstem)) > NAMELEN)
         syntxerr("combined rank stem + rank suffix > NAMELEN");
      strcat(nmptr, rankstem);
      if (ckfordup(eltindex++, eltnames, nmptr) == FALSE)
         putsymtbl(nmptr);
      else {
         GETDECLADDR(&mydecl);
         printf("%s\n", mydecl);
         strcpy(temp, "Multiple element declarations exist for:  ");
         strcat(temp, nmptr);
         terminate(1, temp);
      }
      safewrite(symbfile, &miscflag, sizeof(miscflag));
      nmptr += (NAMELEN + 1);
      safewrite(treefile, savebuff, savebufflen);
   }
   return(eltcount);
}
/* ============================================================ */
/* ============================================================ */
void doelt2(tempnames, tempcount)
char *tempnames;
int tempcount;
{
   extern int symbfile;
   REGISTER int j;

   dominimization();
   docontent(tempnames);
   safewrite(symbfile, (char *) &miscflag, sizeof(miscflag));
   if (INPPS() == EOF)
      terminate(1, "EOF in ELEMENT declaration");
   if ((j = jgetc()) == '-'){
      ADDSTRING(" -");
      doexclusions(tempnames, tempcount);
   }
   else
      jungetc(j);
   if (INPPS() == EOF)
      terminate(1, "EOF in ELEMENT declaration");
   if ((j = jgetc()) == '+'){
      ADDSTRING(" +");
      doinclusions(tempnames, tempcount);
   }
   else
      jungetc(j);
   if (INPPS() == EOF)
      terminate(1, "EOF in ELEMENT declaration");
   if ((j = jgetc()) == MDC) {
      ADDCHAR(MDC);
      return;
   }
   ADDCHAR(j);
   syntxerr("Expected MDC at this point");
}
/* =========================================================== */
dominimization()
{
   int j, pscount;

   miscflag = 0;
   if ((pscount = INPPS()) == EOF)
      terminate(1, "EOF in ELEMENT declaration");
   ADDCHAR(SPACE);
   if ((j = TOUPPER(jgetc())) == EOF)
      terminate(1, "EOF in ELEMENT declaration");
   if ((j != 'O') && (j != '-'))
      syntxerr("minimization must be specified");
   ADDCHAR(j);
   if (j == 'O')
      miscflag |= 1;
   if ((pscount = INPPS()) == EOF)
      terminate(1, "EOF in ELEMENT declaration");
   if (pscount <= 0)
      syntxerr("Expected PS+ at this point");
   ADDCHAR(SPACE);
   if ((j = TOUPPER(jgetc())) == EOF)
      terminate(1, "EOF in ELEMENT declaration");
   if ((j != 'O') && (j != '-')) {
      ADDCHAR(j);
      syntxerr("error in minimization");
   }
   if (j == 'O')
      miscflag |= 2;
   ADDCHAR(j);
   if ((pscount = INPPS()) == EOF)
      terminate(1, "EOF in ELEMENT declaration");
   ADDCHAR(SPACE);
}
/* =========================================================== */
int geteltname(ptr)
char *ptr;
{
   int j;
   char temp[NAMELEN+1], *tmpptr = temp;
   tmpptr = temp;

   if ((j = INPNAME(&tmpptr, NAMELEN, TOUPPER)) == EOF)
      terminate(1, "EOF in ELEMENT declaration");
   if (j >= GOOD) {
      strcpy(ptr, temp);
      return (GOOD);
   }
   return(BAD);
}
/* =========================================================== */
void docontent(tempeltname)
char *tempeltname;
{
   char tempname[NAMELEN + 1];
   extern int symbfile;
   char namearray[NAMELEN + 1], *tmpptr = namearray;
   REGISTER int j;
   char *src, *dest;

   memset(tempname, '\0', sizeof(tempname));
   strncpy(tempname, tempeltname, NAMELEN);
   /* writename(name, 'x', NO);*/
   if ((j = INPNAME(&tmpptr, NAMELEN, TOUPPER)) == EOF)
      terminate(1, "EOF in ELEMENT declaration");
   switch(j) {
   case KW_RCDATA:
      miscflag &= (~1);
      ADDSTRING("RCDATA");
      writename("rCDATA", '*');
      break;
   case KW_CDATA:
      miscflag &= (~1);
      ADDSTRING("CDATA");
      writename("cDATA", '*');
      break;
   case KW_ANY:
      miscflag &= (~1);
      ADDSTRING("ANY");
      writename("aNY", '*');
      break;
   case KW_EMPTY:
      miscflag &= (~1);
      ADDSTRING("EMPTY");
      miscflag = 2 | (1 << 7);
      writename("eMPTY", '1');
      break;
   default:
      if (j >= GOOD)
         syntxerr("Syntax error while processing content model");
      rec = getagroup(1);
      if (countnames(rec) > GRPGTCNT )
         syntxerr("Name count exceed GRPGTCNT");
      if (nestlevel(rec) > GRPLVL)
         syntxerr("Nesting level exceeds GRPLVL");
      getsubexprs(0, strlen(rec) - 1, FALSE, NONE, NONE, NONE, FALSE);
      for (src = dest = rec; *src != '\0';){
         if (*src != EE)
            *dest++ = *src++;
         else
            src++;
      }
      *dest = '\0';
      writecmodel(tempname);
      writecmodel(rec);
   }
}
/* =========================================================== */
/* =========================================================== */
void getsubexprs(low, high, foundconnector, connector, group_oi, curcon, rqflg)
int low, high, foundconnector, connector, group_oi, curcon, rqflg;
{
   int nestlvl, temp, tempoi, reqflg;
   REGISTER int current, oi;
   static int first_elt_is_opt = FALSE;

   FUNCTRACE("getsubexprs");
   if (debug & CMTRC) {
      printf("Enter getsubexprs with:  ");
      for (temp = low; temp <= high; temp++)
         putchar(rec[temp]);
      printf(" foundconnector = %d\n", foundconnector);
   }

   oi = simplify(rec, &low, &high, &foundconnector, &connector);
   if (rqflg == TRUE)
      first_elt_is_opt = TRUE;
   if ((oi == '*') || (oi == '?'))
      first_elt_is_opt = TRUE;
   current = nestlvl = 0;

   nestlvl = 0;
   for(current = low; current <= high; current++) {
      if (rec[current] == GRPO)
         nestlvl++;
      else if (rec[current] == GRPC)
         nestlvl--;
      else if (isconnector(rec[current])) {
         if (nestlvl == 0) {
            chkconnector(rec, current, high);
            if (connector != '&')
               writeconnector(rec[current], oi);
            if ((connector != '&') && (rec[current] == '&')) {
               writecount(andcount(rec, current + 1, high));
               connector = '&';
            }
            if ((rec[current] == '|') || (rec[current] == '&'))
               reqflg = TRUE;
            else
               reqflg = FALSE;
            getsubexprs(low, current - 1, TRUE, NONE, oi, rec[current], reqflg);
            getsubexprs(current + 1, high, FALSE, connector, '1', rec[current], reqflg);
            return;
         }
         else continue;
      }
      else if (isnamerni(&rec[current], NAMELEN)) {
         char *cname;
         int len;
         cname = getnamerni(&rec[current]);
         len = strlen(cname);
         if (*cname == RNI){
            if (strcmp(cname, "#PCDATA") != 0)
               syntxerr("illegal value in content model!");
            else if (isoi(rec[current + len]))
               syntxerr("cannot have occurrence indicator on #PCDATA");
         }
         if (foundconnector) {
            tempoi = updateoi(oi, rec[current + len]);
            writename(cname, tempoi);
            first_elt_is_opt = FALSE;
            return;
         }
         else if (( current + len) >= high) {
            tempoi = updateoi(oi, rec[current + len]);
            writename(cname, tempoi);
            first_elt_is_opt = FALSE;
            return;
         }
      }
   }
}

/* =========================================================== */
/* This function takes expressions like (((X,Y),Z*)+)*
   and simplifies them down to ((X,Y),Z*)*, i.e., it removes 
      excess parentheses at the outermost level and combines
      occurrence indicators.  It returns the OI of the group.
                  */
simplify(temprec, low, high, foundconnector, connector)
char temprec[];
int *low, *high, *foundconnector, *connector;
{
   REGISTER int nestlvl, temp;
   int oi = '1';

   FUNCTRACE("simplify");
   if (debug & SIMPTRC) {
      printf("ENTER SIMPLIFY:  ");
      for (temp = *low; temp <= *high; temp++)
         putchar(temprec[temp]);
      printf(" foundconnector = %d\n", *foundconnector);
      printf(" connector = %d\n", *connector);
   }
SIMPLIFY:
   /* if this group is not bounded by GRPO, GRPC, just return
      the default occurrence indicator */
   if (temprec[*low] != GRPO)
      return(oi);
   if ((temprec[*high] != GRPC) && (temprec[(*high) - 1] != GRPC))
      return(oi);
   /* scan through to see if entire group is bounded by GRPO, GRPC */
   nestlvl = 0;
   for (temp = *low; temp <= *high; temp++) {
      if (temprec[temp] == GRPO)
         nestlvl++;
      else if (temprec[temp] == GRPC)
         nestlvl--;

      if (nestlvl == 0) {
         *foundconnector = FALSE;
         /* we balanced parentheses before the end of the group! */
         if (temp < ((*high) - 1))
            return(oi);
         break;
      }
   }

   /* the whole buffer is bounded by GRPO, GRPC */
   /* indicate we have not yet found a connector */
   *connector = NONE;

   /* if we are on the last byte of the message then we are on a ')'
      and there is no OI to worry about */
   if (temp == *high)
      (*high)--;
      /* else, we are just before the OI for the group */
   else {
      oi = updateoi(oi, temprec[*high]);
      (*high) -= 2;
   }
   /* bump past the first GRPO */
   (*low)++;
   if (debug & SIMPTRC) {
      printf("AFTER SIMPLIFICATION:  ");
      for (temp = *low; temp <= *high; temp++)
         putchar(temprec[temp]);
      printf(" foundconnector = %d\n", *foundconnector);
      printf(" connector = %d\n", *connector);
   }
   goto SIMPLIFY;
}
/* =========================================================== */
int updateoi(oldoi, newoi)
REGISTER int oldoi;
REGISTER char newoi;
{
   FUNCTRACE("updateoi");
   if ((newoi != '+') && (newoi != '*') && (newoi != '?'))
      return(oldoi);
   if (oldoi == '*')
      return('*');
   if (newoi == '*')
      return('*');
   if (newoi == '+')
      if ((oldoi == '+') || (oldoi == '1'))
         return('+');
   if (newoi == '?')
      if ((oldoi == '?') || (oldoi == '1'))
         return('?');
   return('*');
}
/* =========================================================== */
static void writeconnector(cnnctr, oi)
char cnnctr, oi;
{
   extern int treefile;
   TREEREC trec;
   REGISTER int j;
   REGISTER char *src;

   FUNCTRACE("writeconnector");
   memset((char *)&trec, '\0', sizeof(trec));
   /*printf("connector node = %c  (oi = %c)\n", cnnctr, oi);*/
   trec.Tinfo[0] = cnnctr;
   trec.Toi = oi;
   safewrite(treefile, (char *)&trec, sizeof(trec));
   if (saveinfo == ON) {
      for(j = 0, src = (char *)&trec; j < sizeof(trec); j++)
         *savebuffptr++ = *src++;
      savebufflen += sizeof(trec);
   }
}
/* =========================================================== */
void writecount(count)
int count;
{
   extern int treefile;
   REGISTER int j;
   REGISTER char *src;

   FUNCTRACE("writecount");
   safewrite(treefile, (char *)&count, sizeof(count));
   if (saveinfo == ON) {
      for(j = 0, src = (char *)&count; j < sizeof(count); j++)
         *savebuffptr++ = *src++;
      savebufflen += sizeof(count);
   }
}

/* =========================================================== */
static void writename(nodename, oi)
char *nodename;
char oi;
{
   REGISTER char *src;
   extern int treefile;
   TREEREC trec;
   REGISTER int j;
   int len;

   FUNCTRACE("writename");
   memset((char *)&trec, '\0', sizeof(trec));
   len = strlen(nodename);
   if (strcmp(nodename, "#PCDATA") == 0)
      oi = '*';
   for(j = 0; j < len; j++)
      trec.Tinfo[j] = *nodename++;
   trec.Toi = oi;
   trec.Tcontreq = 0x7777;
   safewrite(treefile, (char *)&trec, sizeof(trec));
   if (saveinfo == ON) {
      for(j = 0, src = (char *)&trec; j < sizeof(trec); j++)
         *savebuffptr++ = *src++;
      savebufflen += sizeof(trec);
   }
}

/* ============================================================ */
void putsymtbl(name)
char *name;
{
   SYMBREC symbrec;

   extern int symbfile;
   REGISTER int j;

   FUNCTRACE("putsymbtbl");
   if (strcmp(name, rootelt) == 0) {
      for (j = 0; rootelt[j] != '\0'; j++)
         rootelt[j] = '\0';
      *name |= (1 << 7);
   }
   memset((char *)&symbrec, '\0', sizeof(SYMBREC));
   symbrec.Smin = 0;
   /*printf("symbol = %s\n", name);*/
   for (j = 0; (j < sizeof(symbrec.Sname)) && (name[j] != '\0'); j++)
      symbrec.Sname[j] = name[j];
   safewrite(symbfile, (char *)&symbrec, sizeof(symbrec));
}
/* ============================================================ */
andcount(buff, low, high)
char buff[];
int low;
REGISTER int high;
{
   REGISTER int j;
   int nestlvl, count;

   nestlvl = count = 0;
   FUNCTRACE("andcount");
   for (j = low; j <= high; j++){
      if (buff[j] == '&') {
         if (nestlvl == 0)
            count++;
      }
      else if (buff[j] == '(')
         nestlvl++;
      else if (buff[j] == ')')
         nestlvl--;
   }
   return(count + 2);
}
/* ============================================================ */
void chkconnector(buf, strt, end)
char buf[];
REGISTER int strt, end;
{
   int nestlvl = 0;
   char connector = buf[strt++];

   FUNCTRACE("chkconnector");
   while(strt != end) {
      if (isconnector(buf[strt]) && (nestlvl == 0)) {
         if (buf[strt] != connector)
            syntxerr("different connectors");
      }
      else if (buf[strt] == '(')
         nestlvl++;
      else if (buf[strt] == ')')
         nestlvl--;
      strt++;
   }
}
/* ============================================================ */
/* ============================================================ */
int ckfordup(index, existingnames, name)
REGISTER int index;
char *existingnames, *name;
{
   REGISTER int j;
   for (j = 0; j <index; j++, existingnames += (NAMELEN + 1)) {
      if (strcmp(existingnames, name) == 0)
         return(TRUE);
   }
   strcpy(existingnames, name);
   return(FALSE);
}
/* ============================================================ */
static void doinclusions(names, count)
REGISTER char *names;
int count;
{
   int j, k, i, flag;
   REGISTER int kk;
   char *temp;
   char tempnames[GRPCNT * (NAMELEN + 1)];

   j = getnamegrp(tempnames, ISALPHA, isnmchar);
   for (k = 0; k < count; k++, names += (NAMELEN + 1)) {
      *names &= 0x7f;
      for (kk = 0, flag = OFF; kk < (NAMELEN + 1); kk++) {
         if (*(names + kk) == '\0')
            flag = ON;
         if (flag == ON)
            *(names + kk) = '\0';
      }
      safewrite(inclfile, names, (NAMELEN + 1));
      safewrite(inclfile, (char *) &j, sizeof (j));
      for (temp = tempnames, i = 0; i < j; i++, temp += (NAMELEN + 1))
         safewrite(inclfile, temp, (NAMELEN + 1));
   }
}
/* ============================================================ */
static void doexclusions(names, count)
char *names;
int count;
{
   int j, k, i, kk, flag;
   char *temp;
   char tempnames[GRPCNT * (NAMELEN + 1)];

   j = getnamegrp(tempnames, ISALPHA, isnmchar);
   for (k = 0; k < count; k++, names += (NAMELEN + 1)) {
      *names &= 0x7f;
      for (kk = 0, flag = OFF; kk < (NAMELEN + 1); kk++) {
         if (*(names + kk) == '\0')
            flag = ON;
         if (flag == ON)
            *(names + kk) = '\0';
      }
      safewrite(exclfile, names, (NAMELEN + 1));
      safewrite(exclfile, (char *) &j, sizeof (j));
      for (temp = tempnames, i = 0; i < j; i++, temp += (NAMELEN + 1))
         safewrite(exclfile, temp, (NAMELEN + 1));
   }
}
/* ============================================================ */

/* ============================================================ */
int countnames(mdlptr)
char *mdlptr;
{
#define FIRSTCHAR(x) ((isascii(x)) && (isalpha(x)) || (x == RNI))
#define OTHERCHAR(x) ((FIRSTCHAR(x)) || (isdigit(x)) || (x == '-') || (x == '.'))
   int count = 0;
   while(1) {
      while (FIRSTCHAR(*mdlptr) == FALSE) {
         if(*mdlptr == '\0')
            return(count);
         if (*mdlptr == GRPO)
            count++;
         mdlptr++;
      }
      mdlptr++;
      while (OTHERCHAR(*mdlptr))
         mdlptr++;
      count++;
   }
}
/* ============================================================ */
/* ============================================================ */
int nestlevel(mdl)
char *mdl;
{
   int maxlvl = 0, currlevel = 0;

   while(*mdl != 0) {
      if (*mdl == GRPO) {
         currlevel++;
         if (currlevel > maxlvl)
            maxlvl = currlevel;
      }
      else if (*mdl == GRPC)
         currlevel--;
      mdl++;
   }
   return(maxlvl);
}
/* ============================================================ */
void writecmodel(rec)
char *rec;
{
   char *strstr();
   char buff[1000], *cptr, *dest = buff;
   char *pcdata = "#PCDATA", *src;

   memset(buff, '\0', sizeof(buff));
   while(1) {
      cptr = strstr(rec, pcdata);
      if (cptr == NULL) {
         strcat(buff, rec);
         for (src = dest = buff; *src != '\0';)
            if (isascii(*src))
               *dest++ = *src++;
            else
               src++;
         *dest = '\0';
         fprintf(cmfile, "%s\r\n", buff);
         return;
      }
      while(rec != cptr)
         *dest++ = *rec++;
      strcat(buff, pcdata);
      strcat(buff,"*");
      dest = buff + strlen(buff);
      rec += (strlen(pcdata));
   }
}
/* ============================================================ */
char *strstr(strng, ptrn)
char ptrn[], strng[];
{
   int i, j, k;
   for (i = 0; strng[i] != '\0'; i++) {
      for (j = i, k=0; ptrn[k] != '\0' && strng[j] == ptrn[k]; j++, k++)
         ;
      if (ptrn[k] == '\0')
         return(&strng[i]);
   }
   return(NULL);
}
