/*===========================================================================
  Copyright (C) 1995-2007 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*+++++++++++++++++++++ Module MIDLDB +++++++++++++++++++++++++++++++++++++++
.LANGUAGE   C
.IDENTIFICATION  Module MIDLDB
.AUTHOR   Klaus Banse		ESO - Garching
.KEYWORDS Local Descriptor Blocks
.ENVIRONMENT VMS and UNIX
.COMMENTS
holds clearLDB, cacheLDB, CRELDB, LDBinfo, RDLDB, WRLDB, RDcLDB
.VERSIONS
 [1.30]  861110: move from FORTRAN to C

 091210		last modif
------------------------------------------------------------------------*/
 
#include <fileexts.h>
#include <osyparms.h>
 
 
#define BIT_0  0x1

#define LDBNO  4

static int lastout = 0;
static int chanLDB[LDBNO], blkLDB[LDBNO];
static int statLDB[LDBNO]={0,0,0,0,};
/* statLDB = 0: free	= 1: read LDB	= 2: marked for writing */

static struct LDB_STRUCT   KLDB[LDBNO];
 
/*

*/
 
void clearLDB(chanl)
 
/*++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
.RETURNS
  nothing
--------------------------------------------------*/
 
int   chanl;		/* IN: I/O channel */
 
{
register int  nr;



for (nr=0; nr<LDBNO; nr++)
   {
   if ((statLDB[nr] != 0) && (chanLDB[nr] == chanl))
      {
      statLDB[nr] = 0;
      return;
      }
   }
}

/*

*/
 
int cacheLDB(flag,chanl,blockno,lp)
 
/*++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  create a local descriptor block (LDB) = 4 virtual blocks
.ALGORITHM
  create + initialize a LDB for a bulk data frame
  if the present block points to another (LDB_NEXT != 0) then
  one already exists. otherwise, the logical file extent is
  incremented and the file physically extended, if necessary
.RETURNS
  status:	I*4		return status
--------------------------------------------------*/
 
int   flag;		/* IN: = 1, read
			         2, write 
				 22, create fresh LDB 
				 3, close out (force writing) */
int   chanl;		/* IN: I/O channel */
int   blockno;		/* IN: virtual block no. */
struct LDB_STRUCT     **lp;	/* OUT: pointer to LDB */
 
{
int   lfree, found, status;
register int  nr, *ireg;

char  *cldb;

struct LDB_STRUCT      *ldbp;
 


lfree = -1;

for (nr=0; nr<LDBNO; nr++)
   {
   if (statLDB[nr] == 0)
      lfree = nr;		/* keep last free entry */

   else				/* slot `nr' in use */
      {
      if (chanLDB[nr] == chanl)		/* and same I/O channel */
         {
         *lp = &KLDB[nr];		/* set return pointer to LDB */

         if (blkLDB[nr] == blockno)	/* same block no. */
            {
            if (flag == 1) 		/* read same  LDB */
               return ERR_NORMAL;
            else if (flag == 2) 		/* write same  LDB */
               {
               statLDB[nr] = 2;		/* modify statLDB acc. */
               return ERR_NORMAL;
               }
            else if (flag == 3)			/* force writing */
               {
               cldb = (char *) &KLDB[nr];
               statLDB[nr] = 1;		/* modify statLDB acc. */
#if vms
               return (OSY_WVB(chanl,cldb,LDB_SIZE,blockno));
#else
               return (OSY_WLDB(chanl,cldb,blockno));
#endif
               }
            else				/* only 22 left ... */
               {
               found = nr;
               goto init_LDB;
               }
            }
         else				/* different block no. */
            {
            cldb = (char *) &KLDB[nr];
            if (flag == 3)
               {
               if (blockno != -1)
                  {
                  statLDB[nr] = 1;		/* modify statLDB acc. */
                  blkLDB[nr] = blockno;		/* follow with block no. */
                  }
               else
                  statLDB[nr] = 0;			/* clear entry */
                
#if vms
               return (OSY_WVB(chanl,cldb,LDB_SIZE,blkLDB[nr]));
#else
               return (OSY_WLDB(chanl,cldb,blkLDB[nr]));
#endif
               }
            else if (flag == 2)
               return ERR_INPINV;

 
            /* write LDB before reading again */

            if (statLDB[nr] > 1)
               {
#if vms
               status = OSY_WVB(chanl,cldb,LDB_SIZE,blkLDB[nr]);
#else
               status = OSY_WLDB(chanl,cldb,blkLDB[nr]);
#endif
               if (status != ERR_NORMAL) return status;
               }
            blkLDB[nr] = blockno;

            if (flag != 22) 		/* flag = 1 */
               {
               statLDB[nr] = 1;
#if vms
               return (OSY_RVB(chanl,cldb,LDB_SIZE,blockno));
#else
               return (OSY_RLDB(chanl,cldb,blockno));
#endif
               }

            found = nr;			/* for flag = 22, go on */
            goto init_LDB;
            }
         }
      }
   }


if (flag == 3)
   return ERR_NORMAL;
else if (flag == 2) 
   return ERR_INPINV;

            
/* here, if we have to create a new entry */

if (lfree == -1)
   {					/* we have to clear an entry */
   found = lastout++;
   if (lastout >= LDBNO) lastout = 0;

   cldb = (char *) &KLDB[found];
   if (statLDB[found] > 1)
      {
#if vms
      status = OSY_WVB(chanLDB[found],cldb,LDB_SIZE,blkLDB[found]);
#else
      status = OSY_WLDB(chanLDB[found],cldb,blkLDB[found]);
#endif
      if (status != ERR_NORMAL) return status;
      }				/* statLDB updated below */
   } 
else
   {
   found = lfree;			/* use free entry */
   cldb = (char *) &KLDB[found];
   }

*lp = &KLDB[found];			/* set return pointer to LDB */
chanLDB[found] = chanl;
blkLDB[found] = blockno;
if (flag == 1)				/* only flag = 1 or 22 left */
   {
   statLDB[found] = 1;
#if vms
   return (OSY_RVB(chanl,cldb,LDB_SIZE,blockno));
#else
   return (OSY_RLDB(chanl,cldb,blockno));
#endif
   }
    

/* create a "fresh" LDB */

init_LDB:
statLDB[found] = 2;
ldbp = *lp;
ldbp->BLKNUM = blockno;
ldbp->NEXT = 0;
ireg = ldbp->LDBWORDS.IWORD;
for (nr=0; nr<LDB_NDSCRW; nr+=2)		/*  fill data with 0  */
   {
   *ireg++ = 0;
   *ireg++ = 0;
   }

return ERR_NORMAL;
}


/*		optional debug section

printf("flag=%d, chanl=%d, block=%d written...\n",flag,chanLDB[nr],blockno);

printf("flag=%d (-1), chanl=%d, block=%d written...\n",
       flag,chanLDB[nr],blkLDB[nr]);

printf("flag=%d, chanl=%d, update block=%d written...\n",
       flag,chanLDB[found],blkLDB[found]);
 
printf("flag=%d, chanl=%d, block=%d read...\n",flag,chanl,blockno);
*/

/*

*/
 
int MID_CRELDB(myentry,ldbp)
 
/*++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  create a local descriptor block (LDB) = 4 virtual blocks
.ALGORITHM
  create + initialize a LDB for a bulk data frame
  if the present block points to another (LDB_NEXT != 0) then
  one already exists. otherwise, the logical file extent is
  incremented and the file physically extended, if necessary
.RETURNS
  status:	I*4		return status
--------------------------------------------------*/
 
int   myentry;		/* IN: FCT entry number */
struct LDB_STRUCT    *ldbp;     /* IN: points to LDB */

 
{
int   lpex, status, blockno, chanl, vbn;
 

struct FCT_STRUCT  *fctpntr;

struct FCB_STRUCT  *fcbp;




 
 
fctpntr = FCT.ENTRIES + myentry;			/* init */
chanl = fctpntr->IOCHAN;

 
/* if another LDB exists, write the present one to disk + read the next  */
 
if (ldbp->NEXT != 0)
   {
   vbn = ldbp->BLKNUM;
   status = cacheLDB(2,chanl,vbn,&ldbp);		/* save LDB */

   if (status == ERR_NORMAL) 
      status = cacheLDB(1,chanl,ldbp->NEXT,&ldbp);	/* get next one */

   if (status == ERR_NORMAL) 
      return ERR_NORMAL; 		/*  that's it already folks ... */
    
   goto end_of_it;
   }

 
/*  We have to get a new "block" - may be already allocated
    also increment logical file extension.
    If logical ext. greater than physical ext., extend data frame  */
 
fcbp = fctpntr->FZP;
blockno = fcbp->LEXBDF + 4;			/* 1 LDB = 4 blocks */

if (fcbp->LEXBDF < fcbp->D1BLOCK)		/* no descr after data? */
   {
   if (blockno < fcbp->D1BLOCK) goto upda_LEXBDF;
      
   fcbp->LEXBDF = fcbp->PEXBDF;
   blockno = fcbp->LEXBDF + 4;			/* thus blockno > PEXBDF */
   }


if (blockno > fcbp->PEXBDF) 
   {		/* extend frame for additional descr. blocks (LDBs)  */
   int  exalq;

   if (chanl < 0)			/* we have a "memory file" */
      {
      exalq = 32;				/* increase by 8 LDBs */
      status = MID_VMEM(2,exalq,&chanl);	/* extend virtual memory */
      if (status != ERR_NORMAL)
         {
         MID_ERROR("FSY","MID_CRELDB",status,0);
         return status;				/* status = ERR_MEMOUT */
         }
      fcbp->PEXBDF += exalq;
      }

   else
      {
      exalq = fcbp->PEXBDF + 16;		/* increase by 4 LDBs */

#if vms
      FSY_EXTBDF(fctpntr->FILEIDA,fctpntr->FILEIDB,fctpntr->DEVICE,
           exalq,&lpex,&status);
      if ( (status & BIT_0) == 0 )
#else
      status = FSY_EXTBDF(fctpntr->FILEID,exalq,&lpex);
      if (status != 0)
#endif

         {
         MID_ERROR("FSY","MID_CRELDB/FSY_EXTBDF",status,0);
         return ERR_FRMNAC;
         }
      fcbp->PEXBDF = lpex;
      }
   }
 

/*  update existing LDB + write it to disk  */

upda_LEXBDF:
ldbp->NEXT = fcbp->LEXBDF + 1;

status = cacheLDB(3,chanl,ldbp->BLKNUM,&ldbp);	/* force writing */
if (status == ERR_NORMAL) 			/*  initialize new LDB */
   status = cacheLDB(22,chanl,ldbp->NEXT,&ldbp);
if (status == ERR_NORMAL) 
   {
   fcbp->LEXBDF = blockno;			/* update FCB.LEXBDF */
   fcbp->NOLDB ++; 				/* and LDB count */
   return ERR_NORMAL;
   }
	

/*  something wrong - report it  */
 
end_of_it:
MID_ERROR("MIDAS","MID_CRELDB:",status,0);
return status;
}

/* 

*/

void LDBinfo(chanl,ldbp,indx,alen,extens)

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
Get header info (i.e. next extension) of an atom from the local 
descriptor block, (LDB = 4 virtual blocks)
.ALGORITHM
Each atom in the LDB is preceded by a header of 3 longwords.
The 1. longword holds the no. of data elements in that atom 
The 2. holds the block no.of next extension. If = -1, no extension exists.
The 3. holds the index of next extension 

Ojo: the code must be synchronised with the "header section" of the
     following MID_RDLDB routine !!!
.RETURNS 
nothing
-------------------------------------------------------------------------*/

int   chanl	/* IN : channel no. of frame */;
struct LDB_STRUCT    *ldbp;	/* IN: points to LDB */
int   indx	/* IN : word # in LDB, where atom is to be read */;
int   *alen	/* IO : no. of values required, will be set to actual length \
                        of atom starting from pos. 1 specified via first \
			if = 0, only the header will be returned */;
int   *extens	/* OUT: block no. + indx of next extension */;

{

/* get 1. header longword = actual length */

*alen = ldbp->LDBWORDS.IWORD[indx];


/*  get 2. header longword */
 
if (indx >= LDB_NDSCRW1) 
   {
   (void) cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx = -1;					/* so indx will be 0  */
   }

*extens = ldbp->LDBWORDS.IWORD[++indx];	/* that's the next extension */
 

/*  get 3. header longword */

if (indx >= LDB_NDSCRW1) 
   {
   (void) cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx = -1;					/* so indx will be 0  */
   }
					/* the index of next extension */
*(extens+1) = ldbp->LDBWORDS.IWORD[++indx];
}

/* 

*/

void MID_RDLDB(chanl,ldbp,indx,typ,iatom,ratom,first,alen,extens)

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
read an atom from the local descriptor block, LDB = 4 virtual blocks
.ALGORITHM
Each atom in the LDB is preceded by a header of 3 longwords.
The 1. longword holds the no. of data elements in that atom 
The 2. holds the block no.of next extension. If = -1, no extension exists.
The 3. holds the index of next extension 

Ojo: the code of the "header section" must be synchronised with the
     routine LDBinfo above  !!!
.RETURNS 
nothing
-------------------------------------------------------------------------*/

int   chanl	/* IN : channel no. of frame */;
struct LDB_STRUCT    *ldbp;	/* IN: points to LDB */
int   indx	/* IN : word # in LDB, where atom is to be read */;
int   typ	/* IN : type of atom - 1=I*4,  2=R*4  */;
int   *iatom	/* OUT: receives integer atom */;
float  *ratom	/* OUT: receives real atom */;
int   first	/* IN : 1st  position of descriptor data to be accessed */;
int   *alen	/* IO : no. of values required, will be set to actual length 
                        of atom starting from pos. 1 specified via first 
			if = 0, only the header will be returned */;
int   *extens	/* OUT: block no. + indx of next extension */;

{
int   rlen, left, from;
register int nr;
 

 




/* get 1. header longword = actual length */

rlen = *alen;
*alen = ldbp->LDBWORDS.IWORD[indx];


/*  get 2. header longword */
 
if (indx >= LDB_NDSCRW1) 
   {
   (void) cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx = -1;					/* so indx will be 0  */
   }

*extens = ldbp->LDBWORDS.IWORD[++indx];	/* that's the next extension */
 

/*  get 3. header longword */

if (indx >= LDB_NDSCRW1) 
   {
   (void) cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx = -1;					/* so indx will be 0  */
   }
					/* the index of next extension */
*(extens+1) = ldbp->LDBWORDS.IWORD[++indx];

/* this check left in for backwards compatibility and should be
   taken out one day... */
if (rlen < 1) return;		/* we just wanted the next extension*/

 
*alen -= (first - 1);
 

/*  increment indx + read next LDB, if necessary */
 
indx += first;
while (indx > LDB_NDSCRW1)
   {
   (void) cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx -= LDB_NDSCRW;
   }


/*  indx is now in [0,509] - initialize counters   */
 
if (*alen > rlen)
   *alen = rlen;		/* total length to be transmitted */
else
   rlen = *alen;
from = 0;


/* branch according to 'typ'  */
 
more_data:
left = LDB_NDSCRW - indx ;		/* no. of lw's left */
if (rlen < left) left = rlen;		/* transfer in this blockno */

if (typ == 1)			/* integer atom */
   {
   register int   *result, *ireg;

   result = iatom + from;
   ireg = ldbp->LDBWORDS.IWORD + indx;
   for (nr=0; nr<left; nr++) *result++ = *ireg++;
   }

else 				/* real atom */
   {
   register float   *result, *rreg;

   result = ratom + from;
   rreg = ldbp->LDBWORDS.RWORD + indx;
   for (nr=0; nr<left; nr++) *result++ = *rreg++;
   }
 

/*  read next LDB, if necessary */
 
if (rlen > left)
   {			/* if ((indx+left) > LDB_NDSCRW1) always true here! */
   (void) cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx = 0;
 
   rlen -= left;
   from += left;
   goto more_data;			/* get more data */
   }

}
 
/*

*/

void MID_WRLDB(chanl,ldbp,indx,typ,iatom,ratom,catom,conflg,first,alen,extens)

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
write into an existing atom in the LDB (only the data is updated)
and return atom length (in data values) and next extension of atom
.ALGORITHM
Each atom in the LDB is preceded by a header of three 4-byte words
The 1. word holds the length of the atom in data elements,
the 2. holds the block no. of next extension. If = -1, no extension exists.
the 3. holds the indx of next extension 
.RETURNS 
nothing
-------------------------------------------------------------------------*/

int   chanl	/* IN : channel no. of frame */;
struct LDB_STRUCT    *ldbp;     /* IN: points to LDB */
int   indx	/* IN : index in current LDB  */;
int   typ	/* IN : type of atom 1=I*4, 2=R*4, 3=character data */;
int   *iatom	/* IN : contains integer atom */;
char   *catom	/* IN : contains character atom */;
float  *ratom	/* IN : contains real atom */;
int   conflg    /* IN : constant flag = 1 (yes), 0 = (no) */;
int   first	/* IN : position of descriptor data to be accessed */;
int   *alen	/* IO : no. of values required will be set to actual no. of \
			values modified, starting from 1st position specified \
                        via first */;
int   *extens	/* IO : returns block + indx of extension */;

{
int   rlen, fcharm1=0;
int   left, slen, from;
register int  nr;
 


 
rlen = *alen;
			/* get 1. header 4-byte word = actual length */
*alen = ldbp->LDBWORDS.IWORD[indx];


/*  get 2. header 4-byte words */
 
if (indx >= LDB_NDSCRW1) 
   {
   (void) cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx = -1;					/* so indx will be 0  */
   }
					/* that's the next extension */
*extens = ldbp->LDBWORDS.IWORD[++indx];
 

/*  get 3. header 4-byte words */

if (indx == LDB_NDSCRW1) 
   {
   (void) cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx = -1;					/* so indx will be 0  */
   }
						/* indx of next extension */
*(extens+1) = ldbp->LDBWORDS.IWORD[++indx]; 


/*  for character data, split offset into (integer) 4-byte words + bytes  */
 
nr = first - 1;
if (typ == 3)
   {
   fcharm1 = nr % II_SIZE;
   indx += ((nr / II_SIZE) + 1);
   }
else
   indx += first;
 
*alen -= nr;


/*  increment indx + read next LDB, if necessary */
 
while (indx > LDB_NDSCRW1)
   {
   (void) cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx -= LDB_NDSCRW;
   }


/*  indx is now in [0,509] - initialize counters   */
 
if (*alen > rlen) 
   *alen = rlen;		/* total length to be transmitted */
else
   rlen = *alen;	
from = 0;


/* branch according to data type  */
 
more_data:
left = LDB_NDSCRW - indx;		/* no. of int's left */


/* check for each type for constant flag */

if (typ == 1)			/* integer atom */
   {
   register int   *source, *ireg;

   if (rlen < left)
      slen = rlen;		/* transfer in this blockno */
   else
      slen = left;
   source = iatom + from;
   ireg = ldbp->LDBWORDS.IWORD + indx;

   if (conflg == 1)
      {
      register int  ival;

      ival = *source;
      for (nr=0;nr<slen;nr++) *ireg++ = ival;
      }
   else
      {
      for (nr=0;nr<slen;nr++) *ireg++ = *source++;
      }

   indx += slen;			/*  update pointers */
   }
 
else if (typ == 2)		/* real atom */
   {
   register float   *source, *rreg;

   if (rlen < left)
      slen = rlen;              /* transfer in this blockno */
   else
      slen = left;
   source = ratom + from;
   rreg = ldbp->LDBWORDS.RWORD + indx;
   if (conflg == 1)
      {
      register float rval;
 
      rval = *source;
      for (nr=0;nr<slen;nr++) *rreg++ = rval;
      }
   else
      {
      for (nr=0;nr<slen;nr++) *rreg++ = *source++;
      }

   indx += slen;			/*  update pointers */
   }

else				/* character atom  */
   {
   register char   *cpntra, *cpntrb;

   left = left*II_SIZE - fcharm1;
   if (rlen > left)
      slen = left;
   else
      slen = rlen;
 
   cpntra = (char *) &(ldbp->LDBWORDS);
   cpntra += (indx*II_SIZE + fcharm1);		/* point to first char. */
   cpntrb = catom + from;

   if (conflg == 1)
      {
      register char  cval;

      cval = *cpntrb;
      for (nr=0;nr<slen;nr++) *cpntra++ = cval;
      }
   else
      (void) memcpy(cpntra,cpntrb,(size_t)slen);

   fcharm1 = 0;		/* now always at a 4-byte word boundary */
   indx += (slen+(II_SIZE-1))/II_SIZE ;
   }


(void) cacheLDB(2,chanl,ldbp->BLKNUM,&ldbp);	/*  save current LDB */

if (rlen > slen)
   {			/* if (indx > LDB_NDSCRW1) always true here! */
   (void) cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx = 0;
 
   rlen -= slen;
   from += slen;
   goto more_data;				/* write more data */
   }
}
 
/*

*/

void MID_RDcLDB(chanl,ldbp,indx,catom,first,alen,extens)

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
read a character atom from the local descriptor block, LDB = 4 virtual blocks
.ALGORITHM
Each atom in the LDB is preceded by a header of 3 longwords.
The 1. longword holds the no. of data elements in that atom 
The 2. holds the block no.of next extension. If = -1, no extension exists.
The 3. holds the index of next extension 

Ojo: the code of the "header section" must be synchronised with the
     routine LDBinfo above  !!!
.RETURNS 
nothing
-------------------------------------------------------------------------*/

int   chanl	/* IN : channel no. of frame */;
struct LDB_STRUCT    *ldbp;	/* IN: points to LDB */
int   indx	/* IN : word # in LDB, where atom is to be read */;
char   *catom	/* OUT: receives character atom */;
int   first	/* IN : 1st  position of descriptor data to be accessed */;
int   *alen	/* IO : no. of values required, will be set to actual length 
                        of atom starting from pos. 1 specified via first 
			if = 0, only the header will be returned */;
int   *extens	/* OUT: block no. + indx of next extension */;

{
int   felem, rlen, fcharm1;
int   left, status, mx;
 
register char  *cpntra, *cpntrb;

 


rlen = *alen;


/* get 1. header longword = actual length */

*alen = ldbp->LDBWORDS.IWORD[indx];


/*  get 2. header longword */
 
if (indx >= LDB_NDSCRW1) 
   {
   status = cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx = -1;					/* so indx will be 0  */
   }

*extens = ldbp->LDBWORDS.IWORD[++indx];	/* that's the next extension */
 

/*  get 3. header longword */

if (indx >= LDB_NDSCRW1) 
   {
   status = cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx = -1;					/* so indx will be 0  */
   }
					/* the index of next extension */
*(extens+1) = ldbp->LDBWORDS.IWORD[++indx];

 
/*  split offset into longwords + bytes  */
 
mx = first - 1;
felem = mx / II_SIZE;				/* quotient (integer) */
fcharm1 = mx % II_SIZE;				/* remainder */
felem ++ ;
 

/*  increment indx + read next LDB, if necessary */
 
indx += felem;
while (indx > LDB_NDSCRW1)
   {
   status = cacheLDB(1,chanl,ldbp->NEXT,&ldbp);
   indx -= LDB_NDSCRW;
   }

cpntrb = catom;			/* point to output char. buffer */


/*  indx is now in [0,509] - loop until we got all data */
 
*alen -= mx;
if (*alen > rlen)
   *alen = rlen;		/* total length can be transmitted */
else
   rlen = *alen;		/* total length is truncated */

cpntra = (char *)&(ldbp->LDBWORDS);
cpntra += (indx*II_SIZE + fcharm1);		/* point to first char. */

left = (LDB_NDSCRW - indx)*II_SIZE - fcharm1;	/* no. of chars left */

more_data:
if (rlen <= left)
   {
   (void) memcpy(cpntrb,cpntra,(size_t)rlen);
   return;
   }

(void) memcpy(cpntrb,cpntra,(size_t)left);	/* get as much as possible */
(void) cacheLDB(1,chanl,ldbp->NEXT,&ldbp);	/* read next LDB  */
cpntra = (char *)&(ldbp->LDBWORDS);		/* changes after cacheLDB */
rlen -= left;				/* update remaining size */
cpntrb += left;				/* and follow with pointer */
					/* now always at lword boundary */
left = LDB_NDSCRW*II_SIZE;		/* new no. of chars left */
goto more_data;
}
 
