// DATABASE INITIALIZATION
  
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "adv_func.h"
#include "adv_util.h"
#include "adv_readdb.h"
  
// DESCRIPTION OF THE DATABASE FORMAT
//
//
// THE DATA FILE CONTAINS SEVERAL SECTIONS.  EACH BEGINS WITH A LINE CONTAINING
// A NUMBER IDENTIFYING THE SECTION, AND ENDS WITH A LINE CONTAINING "-1".
//
// SECTION 1: LONG FORM DESCRIPTIONS.  EACH LINE CONTAINS A LOCATION NUMBER,
//    A TAB, AND A LINE OF TEXT.  THE SET OF (NECESSARILY ADJACENT) LINES
//    WHOSE NUMBERS ARE X FORM THE LONG DESCRIPTION OF LOCATION X.
// SECTION 2: SHORT FORM DESCRIPTIONS.  SAME FORMAT AS LONG FORM.  NOT ALL
//    PLACES HAVE SHORT DESCRIPTIONS.
// SECTION 3: TRAVEL TABLE.  EACH LINE CONTAINS A LOCATION NUMBER (X), A SECOND
//    LOCATION NUMBER (Y), AND A LIST OF MOTION NUMBERS (SEE SECTION 4).
//    EACH MOTION REPRESENTS A VERB WHICH WILL GO TO Y IF CURRENTLY AT X.
//    Y, IN TURN, IS INTERPRETED AS FOLLOWS.  LET M=Y/1000, N=Y MOD 1000.
//          IF N<=300   IT IS THE LOCATION TO GO TO.
//          IF 300<N<=500     N-300 IS USED IN A COMPUTED GOTO TO
//                            A SECTION OF SPECIAL CODE.
//          IF N>500    MESSAGE N-500 FROM SECTION 6 IS PRINTED,
//                            AND HE STAYS WHEREVER HE IS.
//    MEANWHILE, M SPECIFIES THE CONDITIONS ON THE MOTION.
//          IF M=0            IT"S UNCONDITIONAL.
//          IF 0<M<100  IT IS DONE WITH M% PROBABILITY.
//          IF M=100    UNCONDITIONAL, BUT FORBIDDEN TO DWARVES.
//          IF 100<M<=200     HE MUST BE CARRYING OBJECT M-100.
//          IF 200<M<=300     MUST BE CARRYING OR IN SAME ROOM AS M-200.
//          IF 300<M<=400     PROP(M MOD 100) MUST *NOT* BE 0.
//          IF 400<M<=500     PROP(M MOD 100) MUST *NOT* BE 1.
//          IF 500<M<=600     PROP(M MOD 100) MUST *NOT* BE 2, ETC.
//    IF THE CONDITION (IF ANY) IS NOT MET, THEN THE NEXT *DIFFERENT*
//    "DESTINATION" VALUE IS USED (UNLESS IT FAILS TO MEET *ITS* CONDITIONS,
//    IN WHICH CASE THE NEXT IS FOUND, ETC.).  TYPICALLY, THE NEXT DEST WILL
//    BE FOR ONE OF THE SAME VERBS, SO THAT ITS ONLY USE IS AS THE ALTERNATE
//    DESTINATION FOR THOSE VERBS.  FOR INSTANCE:
//          15    110022      29    31    34    35    23    43
//          15    14    29
//    THIS SAYS THAT, FROM LOC 15, ANY OF THE VERBS 29, 31, ETC., WILL TAKE
//    HIM TO 22 IF HE"S CARRYING OBJECT 10, AND OTHERWISE WILL GO TO 14.
//          11    303008      49
//          11    9     50
//    THIS SAYS THAT, FROM 11, 49 TAKES HIM TO 8 UNLESS PROP(3)=0, IN WHICH
//    CASE HE GOES TO 9.  VERB 50 TAKES HIM TO 9 REGARDLESS OF PROP(3).
// SECTION 4: VOCABULARY.  EACH LINE CONTAINS A NUMBER (N), A TAB, AND A
//    FIVE-LETTER WORD.  CALL M=N/1000.  IF M=0, THEN THE WORD IS A MOTION
//    VERB FOR USE IN TRAVELLING (SEE SECTION 3).  ELSE, IF M=1, THE WORD IS
//    AN OBJECT.  ELSE, IF M=2, THE WORD IS AN ACTION VERB (SUCH AS "CARRY"
//    OR "ATTACK").  ELSE, IF M=3, THE WORD IS A SPECIAL CASE VERB (SUCH AS
//    "DIG") AND N MOD 1000 IS AN INDEX INTO SECTION 6.  OBJECTS FROM 50 TO
//    (CURRENTLY, ANYWAY) 79 ARE CONSIDERED TREASURES (FOR PIRATE, CLOSEOUT).
// SECTION 5: OBJECT DESCRIPTIONS.  EACH LINE CONTAINS A NUMBER (N), A TAB,
//    AND A MESSAGE.  IF N IS FROM 1 TO 100, THE MESSAGE IS THE "INVENTORY"
//    MESSAGE FOR OBJECT N.  OTHERWISE, N SHOULD BE 000, 100, 200, ETC., AND
//    THE MESSAGE SHOULD BE THE DESCRIPTION OF THE PRECEDING OBJECT WHEN ITS
//    PROP VALUE IS N/100.  THE N/100 IS USED ONLY TO DISTINGUISH MULTIPLE
//    MESSAGES FROM MULTI-LINE MESSAGES; THE PROP INFO ACTUALLY REQUIRES ALL
//    MESSAGES FOR AN OBJECT TO BE PRESENT AND CONSECUTIVE.  PROPERTIES WHICH
//    PRODUCE NO MESSAGE SHOULD BE GIVEN THE MESSAGE ">$<".
// SECTION 6: ARBITRARY MESSAGES.  SAME FORMAT AS SECTIONS 1, 2, AND 5, EXCEPT
//    THE NUMBERS BEAR NO RELATION TO ANYTHING (EXCEPT FOR SPECIAL VERBS
//    IN SECTION 4).
// SECTION 7: OBJECT LOCATIONS.  EACH LINE CONTAINS AN OBJECT NUMBER AND ITS
//    INITIAL LOCATION (ZERO (OR OMITTED) IF NONE).  IF THE OBJECT IS
//    IMMOVABLE, THE LOCATION IS FOLLOWED BY A "-1".  IF IT HAS TWO LOCATIONS
//    (E.G. THE GRATE) THE FIRST LOCATION IS FOLLOWED WITH THE SECOND, AND
//    THE OBJECT IS ASSUMED TO BE IMMOVABLE.
// SECTION 8: ACTION DEFAULTS.  EACH LINE CONTAINS AN "ACTION-VERB" NUMBER AND
//    THE INDEX (IN SECTION 6) OF THE DEFAULT MESSAGE FOR THE VERB.
// SECTION 9: LIQUID ASSETS, ETC.  EACH LINE CONTAINS A NUMBER (N) AND UP TO 20
//    LOCATION NUMBERS.  BIT N (WHERE 0 IS THE UNITS BIT) IS SET IN COND(LOC)
//    FOR EACH LOC GIVEN.  THE COND BITS CURRENTLY ASSIGNED ARE:
//          0     LIGHT
//          1     IF BIT 2 IS ON: ON FOR OIL, OFF FOR WATER
//          2     LIQUID ASSET, SEE BIT 1
//          3     PIRATE DOESN"T GO HERE UNLESS FOLLOWING PLAYER
//    OTHER BITS ARE USED TO INDICATE AREAS OF INTEREST TO "HINT" ROUTINES:
//          4     TRYING TO GET INTO CAVE
//          5     TRYING TO CATCH BIRD
//          6     TRYING TO DEAL WITH SNAKE
//          7     LOST IN MAZE
//          8     PONDERING DARK ROOM
//          9     AT WITT"S END
//    COND(LOC) IS SET TO 2, OVERRIDING ALL OTHER BITS, IF LOC HAS FORCED
//    MOTION.
// SECTION 10: CLASS MESSAGES.  EACH LINE CONTAINS A NUMBER (N), A TAB, AND A
//    MESSAGE DESCRIBING A CLASSIFICATION OF PLAYER.  THE SCORING SECTION
//    SELECTS THE APPROPRIATE MESSAGE, WHERE EACH MESSAGE IS CONSIDERED TO
//    APPLY TO PLAYERS WHOSE SCORES ARE HIGHER THAN THE PREVIOUS N BUT NOT
//    HIGHER THAN THIS N.  NOTE THAT THESE SCORES PROBABLY CHANGE WITH EVERY
//    MODIFICATION (AND PARTICULARLY EXPANSION) OF THE PROGRAM.
// SECTION 11: HINTS.  EACH LINE CONTAINS A HINT NUMBER (CORRESPONDING TO A
//    COND BIT, SEE SECTION 9), THE NUMBER OF TURNS HE MUST BE AT THE RIGHT
//    LOC(S) BEFORE TRIGGERING THE HINT, THE POINTS DEDUCTED FOR TAKING THE
//    HINT, THE MESSAGE NUMBER (SECTION 6) OF THE QUESTION, AND THE MESSAGE
//    NUMBER OF THE HINT.  THESE VALUES ARE STASHED IN THE "HINTS" ARRAY.
//    HNTMAX IS SET TO THE MAX HINT NUMBER (<= HNTSIZ).  NUMBERS 1-3 ARE
//    UNUSABLE SINCE COND BITS ARE OTHERWISE ASSIGNED, SO 2 IS USED TO
//    REMEMBER IF HE"S READ THE CLUE IN THE REPOSITORY, AND 3 IS USED TO
//    REMEMBER WHETHER HE ASKED FOR INSTRUCTIONS (GETS MORE TURNS, BUT LOSES
//    POINTS).
// SECTION 12: MAGIC MESSAGES. IDENTICAL TO SECTION 6 EXCEPT PUT IN A SEPARATE
//    SECTION FOR EASIER REFERENCE.  MAGIC MESSAGES ARE USED BY THE STARTUP,
//    MAINTENANCE MODE, AND RELATED ROUTINES.
// SECTION 0: END OF DATABASE.
// READ THE DATABASE IF WE HAVE NOT YET DONE SO

// do "retail" memory from what we got "wholesale" with malloc
char * jalloc(int len)
{
  char *tp;              // temp

  if (gm.nblock + len < gm.eblock)
  {
    tp = gm.nblock;         // save this to give to caller
    gm.nblock += len;       // move next-pointer up
    return tp;
  }
  else 
    return NULL;         // indicate not enough space! 
}

void enter_db(int sectno, int indx, int alt, char *bfr)
{
  char *tptr;         // temp pointer for use with malloc
  char *bptr, *bptr1;          // temp pointer 
  int blen;                    // temp str len for bptr/bptr1
  int tkcount;                 // count of keywords on each travel/cond line
  int tkx;                     // index for tk[]
  int tk[21];                  // read travel/cond keywords here
  int val1, val2, count;       // for reading in other values
  int tloc;                    // temps
  int cndx;                    // for tracking multiple entries with same gm.key
  static int prevalt = -1;     // check multiple "alt" descriptions of objects

  // check for all the "text" sections
  if ( (sectno == 1) ||
       (sectno == 2) ||
       (sectno == 5) ||
       (sectno == 6) ||
       (sectno == 10) ||
       (sectno == 12) )
  {  
    if (!(tptr = jalloc(strlen(bfr)+1)))
    {
      printf("INSUFFICIENT MEMORY AVAILABLE FOR DATA BASE (SECTION %i, INDEX %i)!\n",sectno,indx);
      exit(1);
    }
    // if we get through to here, we have the memory - use it
    strcpy(tptr,bfr);      // copy the data to allocated memory     
    switch (sectno)
    { 
      case 1:    // location descriptions - long
        gm.ltext[indx] = tptr;
        break;
      case 2:    // location descriptions - short 
        gm.stext[indx] = tptr;
        break;
      case 5:    // gm.object descriptions
        // note - this is NOT correct - need to handle extended description
        if (alt == -1)
        {
          gm.ptext[indx] = tptr;
          prevalt = -1;
        }
        else
        {
          if (alt != prevalt)    // could be -1 or a different alt
          { 
            gm.ptexta[indx][alt] = tptr;
            prevalt = alt;
          }
          else  // alt == prevalt
          {
            // we have allocated enough room with previous item and this item, and
            //   we know the two pieces are contiguous, but we need to concatenate 
            //   the two strings. Solution: replace the '\0' at the end of the
            //   previous string with a blank, and all will be OK ... I hope!
            *(tptr-1) = ' ';
          }
        }
        break;
      case 6:    // miscellaneous gm.messages
        gm.rtext[indx] = tptr;
        break;
      case 10:   // user class gm.messages (for end of game)
        gm.cval[++gm.clsses] = indx;
        gm.ctext[gm.clsses] = tptr;
        break;
      case 12:   // gm.magic gm.messages
        gm.mtext[indx] = tptr;
        break;
    }
  }
  else
  {
    // do this later
    switch (sectno)
    {
      // THE STUFF FOR SECTION 3 IS ENCODED HERE.  EACH "FROM-LOCATION" GETS A
      // CONTIGUOUS SECTION OF THE "TRAVEL" ARRAY.  EACH ENTRY IN TRAVEL IS
      // NEWLOC*1000 + KEYWORD (FROM SECTION 4, MOTION VERBS), AND IS NEGATED IF
      // THIS IS THE LAST ENTRY FOR THIS LOCATION.  KEY(N) IS THE INDEX IN TRAVEL
      // OF THE FIRST OPTION AT LOCATION N.
      // note that **SOME** of the gm.destinations have an extra value (*1000) added,
      //   presumably this will be cleared up later
      // let's do a less hogm.key implementation here:
      //   gm.travel will be gm.newloc
      //   gm.travwd will be the gm.keyword
      //   gm.travlast will be a BOOL, TRUE for last entry 
      // and note that we have to pull gm.newloc and all the gm.keywords out of bfr
      //   (we will never get here on the -1 end-of-section line)
      case 3:
        tkcount = sscanf(bfr,"%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i",
                       &gm.newloc, &tk[1], &tk[2], &tk[3], &tk[4], &tk[5],
                       &tk[6], &tk[7], &tk[8], &tk[9], &tk[10],
                       &tk[11], &tk[12], &tk[13], &tk[14], &tk[15],
                       &tk[16], &tk[17], &tk[18], &tk[19], &tk[20]);
        if (gm.key[indx] != 0)
          gm.travlast[gm.trvs-1] = FALSE;  // oops! prev wasn't really last!
        else gm.key[indx] = gm.trvs;
        for (tkx=1;tkx<=tkcount-1;tkx++)  // words read is items read minus 1 (gm.newloc)
        { 
          gm.travel[gm.trvs] = gm.newloc;
          gm.travwd[gm.trvs] = tk[tkx];
          if (++gm.trvs == TRVSIZ) bug(3);
        }
        gm.travlast[gm.trvs-1] = TRUE;  // mark last in loop as (tentatively) last for indx 
        break;

      // HERE WE READ IN THE VOCABULARY.  KTAB(N) IS THE WORD NUMBER, ATAB(N) IS
      // THE CORRESPONDING WORD.  THE -1 AT THE END OF SECTION 4 IS LEFT IN KTAB
      // AS AN END-MARKER.  THE WORDS ARE GIVEN A MINIMAL HASH TO MAKE READING THE
      // CORE-IMAGE HARDER.  NOTE THAT "/7-08" HAD BETTER NOT BE IN THE LIST, 
      // SINCE IT COULD HASH TO -1.
      // now: we are gm.entering with one line already read in, and we know that
      //   "indx" is valid (not 0 or -1)
      // "bfr" should contain exactly the word we want to put in the table
      // make "gm.tabndx" a static variable instead of a loop index
      // temporarily we will ignore the "hash"
      // maybe we will sort this thing later, and use a binary search lookup
      //   ... or maybe not if there is some ordering dependency
      case 4:
        bptr = bfr;
        cndx = 0; 
        while (*bptr == ' ' ) bptr++;  // scan to 1st/next non-blank
        while (*bptr != '\0')         // exit loop on end of line
        {
          bptr1 = bptr+1;
          while (*bptr1 != ' ') bptr1++;   // scan to next blank
          blen = bptr1 - bptr;
          strncpy(gm.atab[gm.tabndx],bptr,blen);
          gm.atab[gm.tabndx][blen] = '\0';       // terminate gm.properly
          //  gm.atab[gm.tabndx] = gm.atab[gm.tabndx].xor."phrog";
          gm.ktab[gm.tabndx++] = indx;
          bptr = bptr1;                 // now go to blank after word just done ...
          while (*bptr == ' ') bptr++;  //   and scan to 1st/next non-blank
        }
        break; 

      // READ IN THE INITIAL LOCATIONS FOR EACH OBJECT.  ALSO THE IMMOVABILITY INFO.
      // PLAC CONTAINS INITIAL LOCATIONS OF OBJECTS.  FIXD IS -1 FOR IMMOVABLE
      // OBJECTS (INCLUDING THE SNAKE), OR = SECOND LOC FOR TWO-PLACED OBJECTS.
      // the object number is in indx, bfr has to be sscanf'd to get loc1 and loc2
      // note that some lines only have loc1, so we have to check and make sure
      //   there is a loc2 before using it (else leave gm.fixd with initialized zero)
      case 7:
        count = sscanf(bfr,"%i%i",&val1,&val2);
        gm.plac[indx] = val1;
        if (count > 1)
          gm.fixd[indx] = val2;
        break; 

      // READ DEFAULT MESSAGE NUMBERS FOR ACTION VERBS, STORE IN ACTSPK.
      // similar to section 7 - gm.verb is in indx, must get val1 from bfr
      case 8:
        count = sscanf(bfr,"%i",&val1);
        gm.actspk[indx] = val1;
        break; 

      // READ INFO ABOUT AVAILABLE LIQUIDS AND OTHER CONDITIONS, STORE IN COND.
      // this looks a lot like the travel data, so use tk to read in
      case 9:
        tkcount = sscanf(bfr,"%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i",
                       &tk[1], &tk[2], &tk[3], &tk[4], &tk[5],
                       &tk[6], &tk[7], &tk[8], &tk[9], &tk[10],
                       &tk[11], &tk[12], &tk[13], &tk[14], &tk[15],
                       &tk[16], &tk[17], &tk[18], &tk[19], &tk[20]);
        for (tkx=1;tkx<=tkcount;tkx++)  // words read is items read 
        {
          tloc = tk[tkx];
          if (tloc != 0)      // only useful if it has something in it!
          {
            if (bitset(tloc,indx)) bug(8);    // bit already set. oh, blast!
            gm.cond[tloc] = gm.cond[tloc] | (1 << indx);
          }
        } 
        break; 

      // READ DATA FOR HINTS.
      // there should be 4 items in bfr in all cases
      case 11:
        tkcount = sscanf(bfr,"%i%i%i%i",&tk[1], &tk[2], &tk[3], &tk[4]);
        if (indx != 0)
        {
          if(indx < 0 || indx > HNTSIZ) bug(7);
          for (tkx=1;tkx<=4;tkx++)
            gm.hints[indx][tkx] = tk[tkx];
          gm.hntmax = (gm.hntmax>indx) ? gm.hntmax : indx;
        }
        break; 

    }  // end switch
  } 
}

void read_db(void)
{      
  char bfr[2000], lyn[80], ch;
  int pp;
  int  ix, iy, indx, previndx, altindx, sectno;
  int  in_a_sect = FALSE;
  FILE *infyl;

  printf("INITIALIZING...\n");

  // CLEAR OUT THE VARIOUS TEXT-POINTER ARRAYS.  ALL TEXT IS STORED IN ARRAY
  // LINES; EACH LINE IS PRECEDED BY A WORD POINTING TO THE NEXT POINTER (I.E.
  // THE WORD FOLLOWING THE END OF THE LINE).  THE POINTER IS NEGATIVE IF THIS IS
  // FIRST LINE OF A MESSAGE.  THE TEXT-POINTER ARRAYS CONTAIN INDICES OF
  // POINTER-WORDS IN LINES.
  // **********************************************************************
  // new database construction
  // all text messages will be stored in malloc'd char[] form
  // the ?text arrays will now be char *, each pointing to the proper string
  // (deep six all that A5 crap!)
 
  // STEXT(N) IS SHORT DESCRIPTION OF LOCATION N.

  // LTEXT(N) IS LONG DESCRIPTION. 

  // PTEXT(N) POINTS TO MESSAGE FOR PROP(N)=0.
  //   SUCCESSIVE PROP MESSAGES ARE FOUND BY CHASING POINTERS.  

  // RTEXT CONTAINS SECTION 6"S STUFF. 

  // CTEXT(N) POINTS TO A PLAYER-CLASS MESSAGE. 

  // MTEXT IS FOR SECTION 12.  

  // WE ALSO CLEAR COND.  SEE DESCRIPTION OF SECTION 9 FOR DETAILS.
  //   (still need to figure out what "gm.cond" looks like, leave it int for now)

  // note all this stuff is 1-origin ... too hard to change that
  for (ix=1;ix<=LOCSIZ;ix++)  
  {
      gm.ltext[ix] = NULL;       // database section 1
      gm.stext[ix] = NULL;       // database section 2
      gm.cond[ix] = 0;           // flag bits - forced, liquid, etc.
   }
   gm.cond[0] = 0;               // this one, we need to be sure index 0 has zero in it

  for (ix=1;ix<=TRVSIZ;ix++)  // database section 3, gm.travel
  {
    gm.travel[ix] = gm.travwd[ix] = 0;
    gm.travlast[ix] = FALSE;
  }
  for (ix=1;ix<=TABSIZ;ix++)  // this is logically part of gm.travel, but location-sized
    gm.key[ix] = 0; 

  for (ix=1;ix<=TABSIZ;ix++)  // dgm.atabase section 4, vocabulary
  {
      gm.ktab[ix] = -1;          // be sure 1st unused has a -1 terminator
      gm.cntab[ix] = 0;          // in lieu of adding this * 1000 to gm.ktab?
      gm.atab[ix][0] = '\0';     // all strings gm.null initially
  }

  for (ix=1;ix<=OBJSIZ;ix++)
  {
      gm.ptext[ix] = NULL;       // dgm.atabase section 5
      for (iy=0;iy<OBJALTMAX;iy++)    // yes, THIS is origin zero!
          gm.ptexta[ix][iy] = NULL;    
  }

  for (ix=1;ix<=RTXSIZ;ix++)
      gm.rtext[ix] = NULL;       // dgm.atabase section 6

  for (ix=1;ix<=CLSMAX;ix++)
  { 
      gm.ctext[ix] = NULL;       // dgm.atabase section 10
      gm.cval[ix] = 0;
  }

  for (ix=1;ix<=MAGSIZ;ix++)
      gm.mtext[ix] = NULL;       // dgm.atabase section 12

  gm.setup = 1;
  gm.linuse = 1;
  gm.clsses = 0;           // will increment to put 1st one in class 1
  gm.tabndx = 1;           // index for vocabulary - ready for 1st entry
  gm.trvs = 1;             // index for gm.travel table
  gm.hntmax = 0;           // index for storing hint data

  // string storage
  if ( !(gm.bblock = malloc(LINSIZ*5)) )
  {
    printf("INSUFFICIENT MEMORY AVAILABLE FOR STRING STORAGE DATA BASE!\n");
    exit(1);
  }
  gm.nblock = gm.bblock;
  gm.eblock = gm.bblock + LINSIZ*5 -1;
 
  bfr[0] = '\0';      // buffer starts as empty 
  if ((infyl = fopen(DATFYL,"r")) == NULL)
  {
    printf("ERROR OPENING %s - TERMINATING!\n",DATFYL);
    exit(1);
  }
  // file opened - start reading by priming the loop
  fscanf(infyl,"%i",&indx);
  fgets(lyn,80,infyl);      // swallow rest of "section" line
  while (indx > 0)
  {
    sectno = indx;
    in_a_sect = TRUE;
    // now prime the inner loop
    previndx = -1;             // be sure first line doesn't match
    altindx = -1;              // gm.obj desc only - be sure 1st is primary
    fscanf(infyl,"%i",&indx);
    while (indx != -1)
    {
      do
      { 
        ch = fgetc(infyl);
      }
      while (ch == '\t');  // skip tab(s) between indx and data
      if (ch != '\n')        // if there IS data, read it in
      {
        lyn[0] = ch;         // put the character we read in the buffer
        fgets(lyn+1,80,infyl);   // read rest of data AFTER that char
        while ( (lyn[strlen(lyn)-1] == '\n') ||
                (lyn[strlen(lyn)-1] == '\r') )   // if string ends in EOL,
          lyn[strlen(lyn)-1] = '\0';      //   truncate the last char
       }
      if ( sectno == 4 )  
      {
        pp = strpos(lyn,'(');         // vocab comment
        if (-1 != pp)
          lyn[pp] = '\0';   // truncate comment 
        for (ix=0;ix<strlen(lyn);ix++)
          if ('\t' == lyn[ix]) lyn[ix] = ' ';  // regm.place all tabs with single blanks   
      }
      if (sectno == 3)        // each line is independent, just do it
      {
         strcpy(bfr,lyn);     // put line into buffer 
         enter_db(sectno,indx,altindx,bfr);
         bfr[0] = '\0';       // make sure no data is gm.entered for the "-1"
      }  
      else if (indx != previndx)  // some sections, lines get concatentated
      {
        if (bfr[0] != '\0')  // if there was a previous item, gm.enter it into db  
        {
           if (sectno == 4)  // vocabulary
             strcat(bfr," ");     // make sure there's ALWAYS a trailing blank
           enter_db(sectno,previndx,altindx,bfr);
        }
        strcpy(bfr,lyn);     // put first hunk of item in buffer 
        if ( (sectno == 5) && (indx % 100 == 0) )  // 000, 100, 200, ...
          altindx = indx/100;    // alternate - don't change previndx
        else
        {
          previndx = indx;       // remember which item it is
          altindx = -1;         // new primary, reset alt
        } 
      }
      else
      {
        if ( (sectno == 1) || (sectno == 5) || (sectno == 6) || (sectno == 12) )
          strcat(bfr,"\n");  // gm.message text, put line breaks for readability
        else if (sectno == 4 || sectno == 9)
          strcat(bfr,"    ");   // vocabulary words, put blanks between
        // may add special processing for other sections later 
        strcat(bfr,lyn);     // put next hunk of item in buffer 
      }
      lyn[0] = '\0';         // make sure line buffer is empty
      fscanf(infyl,"%i",&indx);
    }  // end while (indx != -1)
    fgets(lyn,80,infyl);     // swallow rest of -1 "end section" line
    in_a_sect = FALSE;
    // clean up, show last line of section
    if (bfr[0] != '\0')  // if there was a previous item, print it  
    {
      if (sectno == 4)  // vocabulary
        strcat(bfr," ");     // make sure there's ALWAYS a trailing blank
      enter_db(sectno,previndx,altindx,bfr);
      bfr[0] = '\0';
    }
    // read indx for next section (zero is end of data)
    fscanf(infyl,"%i",&indx);
    fgets(lyn,80,infyl);      // swallow rest of "section" line
  }
  printf("COMPLETED READING DATABASE FILE\n");
  fclose(infyl);
}

