/*===========================================================================
  Copyright (C) 1995-2009 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 MIDKEYB +++++++++++++++++++++++++++++++++++++++
.LANGUAGE   C
.IDENTIFICATION  Module MIDKEYB
.AUTHOR   Klaus Banse           ESO - Garching
.KEYWORDS Midas keyword utility routines.
.ENVIRONMENT VMS and UNIX
.COMMENT  
holds DEFKEY, DELKEY, PACKY, TSTKEY
 
.VERSION  [1.00] 920211: pulled out from midkey.c

 090318		last modif
------------------------------------------------------------------------*/
 
 
#include <stdio.h>
#include <string.h>

#include <fileexts.h>
 
 
#define  READ         0
#define  WRITE        1
#define  READ_WRITE   2
#define  APPEND       3
 
#define MAX_LEVEL  25		/* synchronize with midback.h ...*/




static struct KEY_STRUCT  *keypntr;

static char   level[MAX_LEVEL+4] = "ABCDEFGHIJKLMNOPQRSTUVWXY";
			/* synchronize with MID_FNDKEY + CLEAR_LOCAL... */


/*

*/

int MID_TSTKEY(name)

/*++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  check name of local keyword against reserved keyword names
.ALGORITHM
  loop from index 0 to KEYALL->SYSNO and compare the names
.RETURNS
  status:       I*4             Return status
                                if = 0, everything is o.k.
--------------------------------------------------*/

char   *name    /* IN: Name of local keyword */;


{
register int  nr, cdif, kstrn;

char    kname[20];
register char  nc;





cdif = 'A' - 'a';

for (nr=0; nr<=KEY_NAMELEN; nr++)
   {
   nc = name[nr];
   if (nc == '\0')
      {
      kstrn = nr;
      goto step_a;
      }
   if ((nc >= 'a') && (nc <= 'z'))              /* convert to upper case */
      nc += cdif;
   kname[nr] = nc;
   }
return (-1);                                    /* bad (too long) keyname */


step_a:
if (kstrn < KEY_NAMELEN) kname[kstrn++] = ' ';  /* append a blank */


/* loop through KEYNAMES from 0 till KEYALL.SYSNO and compare the key names */

keypntr = KEYALL.KEYNAMES;
for (nr=0; nr<=KEYALL.SYSNO; nr++)
   {
   if (strncmp(keypntr->IDENT,kname,(size_t)kstrn) == 0) return (-1);
   keypntr ++;
   }
return (0);
}

 
/*

*/

#ifdef __STDC__
int MID_DEFKEY(char * name , char loco , char * type , int actval , int * unit)
#else
int MID_DEFKEY(name,loco,type,actval,unit)
/*++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  define + create a user keyword
.ALGORITHM
  check input parameters and update pointers
  do not fill the data block
  also take care of local keywords 
.RETURNS
  status:	I*4		Return status
				if = 0, everything is o.k.
--------------------------------------------------*/
char   *name	/* IN: Name of keyword */;
char   loco	/* IN: = 'L' or 'l' for local keyword */;
char   *type	/* IN: type of keyword = I or R or CHAR*len or D or S */;
int   actval	/* IN: No. of elements in keyword : actual length of 
                       keyword depends on its type */;
int   *unit	/* IN: unit pointer of keyword */;
#endif 

{
int   status, noelem, bytelem, proglevel;
int   byt_len, kstrn, cdif, koffs, pad, tot_len;
int  sizbyt = 0;
register int  nr;

char   k_typ[4], kname[20];
char   kc, loclevel, loclow;
register char  nc;
 

 



/*  name should not begin with '\' (that's how we mark deleted keys...) */
 
if ( *name == '\\')
   {
   status = ERR_INPINV;
   goto end_of_it;
   }
 
cdif = 'A' - 'a';
for (nr=0; nr<=KEY_NAMELEN; nr++)
   {
   nc = name[nr];
   if (nc == '\0')
      {
      kstrn = nr;
      goto step_a;
      }
   if ((nc >= 'a') && (nc <= 'z'))              /* convert to upper case */
      nc += cdif;
   kname[nr] = nc;
   }
return (-1);                                    /* bad (too long) keyname */

 
/*  check type + length  */
 
step_a:
if (kstrn < KEY_NAMELEN) kname[kstrn++] = ' ';  /* append a blank */

MID_TYPCHK(type,k_typ,&bytelem);
if (k_typ[0] == ' ') 			/* invalid type was given... */
   {
   status = ERR_INPINV;
   goto end_of_it;
   }
  
noelem = actval;

 
/* -------------------------- */
/*  now for local keys        */
/* -------------------------- */
 

if ((loco == 'L') || (loco == 'l'))
   {
   proglevel = KIWORDS[OFF_MODE+6] - 1;    /* get current program level */
   if (proglevel < 0)
      {
      status = ERR_INPINV;		/* DEFINE/LOCAL only in procedure !  */
      goto end_of_it;
      }

   loclevel = level[proglevel];
   loclow = loclevel + ('a' - 'A');	/* e.g. 'B' -> 'b' */


   /* loop through LOCNAMES from KEYALL.LOCNO till KEYALL.LOCENT
      and compare the key names as well as the procedure level    */

   keypntr = KEYALL.KEYNAMES + KEYALL.GLOBENT;
   for (nr=KEYALL.GLOBENT; nr<=KEYALL.LOCNO; nr++)  
      {
      if ( (strncmp(keypntr->IDENT,kname,(size_t)kstrn) == 0) )
         {
         kc = keypntr->IDENT[16];
         if ((kc == loclevel) || (kc == loclow)) 
            {					/* name already there... */
            status = ERR_KEYBAD;
            goto end_of_it;
            }
         }
      keypntr ++;
      }


   /* calculate length in bytes + eventual front padding bytes  */

   if (k_typ[0] == 'C')
      {
      pad = 0;
      byt_len = noelem*bytelem;
      }
   else
      {
      int  mm;

      if (k_typ[0] == 'I')
         sizbyt = II_SIZE;
      else if (k_typ[0] == 'R')
         sizbyt = RR_SIZE;
      else if (k_typ[0] == 'D')
         sizbyt = DD_SIZE;
      else 
         sizbyt = SS_SIZE;
 
      /* pad = sizbyt - 1 - (KEYALL.LOCEND % sizbyt); */
      mm = sizbyt - 1;
      pad = mm - (KEYALL.LOCEND & mm);
      byt_len = noelem * sizbyt;
      }


   /*  check, if LOCNAMES full  */

   if (KEYALL.LOCNO >= KEYALL.LOCENT-1)
      {
      MID_PACKY();                 /* try a garbage collection... */
      if (KEYALL.LOCNO >= KEYALL.LOCENT-1)
         {
         status = ERR_KEYOVN;
         goto end_of_it;
         }
      }


   /* look, if LOCWORDS full */

   tot_len = pad + byt_len;
   if (KEYALL.LOCEND+tot_len >= KEYALL.LOCDAT)
      {
      MID_PACKY();                         /* try a garbage collection... */
      if (KEYALL.LOCEND+tot_len >= KEYALL.LOCDAT)
         {
         status = ERR_KEYOVL;
         goto end_of_it;
         }
      }


   /*   everything o.k. - do what's necessary  */

   KEYALL.LOCNO ++;
   koffs = KEYALL.LOCEND + 1;
   KEYALL.LOCEND += tot_len;
   keypntr = KEYALL.KEYNAMES + KEYALL.LOCNO;           /* point to new entry */
   if (loco == 'L') 
      keypntr->IDENT[16] = loclevel;	 /* only defined for this very level */
   else
      keypntr->IDENT[16] = loclow;	    /* also defined for lower levels */
   }
 
 
/* -------------------------- */
/*  now for global keys        */
/* -------------------------- */
 
 
else
   {
 
   /*  calculate length in bytes + eventual front padding bytes  */

   if (k_typ[0] == 'C')
      {
      pad = 0;
      byt_len = noelem*bytelem;
      }
   else
      {
      if (k_typ[0] == 'I')
         sizbyt = II_SIZE;
      else if (k_typ[0] == 'R')
         sizbyt = RR_SIZE;
      else if (k_typ[0] == 'D')
         sizbyt = DD_SIZE;
      else 
         sizbyt = SS_SIZE;

      pad = sizbyt - 1 - (KEYALL.GLOBEND % sizbyt);
      byt_len = noelem * sizbyt;
      }
	
 
   /* check, if KEYNAMES full */
 
   if (KEYALL.GLOBNO >= KEYALL.GLOBENT-1)
      {
      MID_PACKY();			/* try a garbage collection... */
      if (KEYALL.GLOBNO >= KEYALL.GLOBENT-1)
         {
         status = ERR_KEYOVN;
         goto end_of_it;
         }
      }
 
 
   /* look, if KEYWORDS full */
 
   tot_len = pad + byt_len;
   if (KEYALL.GLOBEND+tot_len >= KEYALL.GLOBDAT)
      {
      MID_PACKY();			     /* try a garbage collection... */
      if (KEYALL.GLOBEND+tot_len >= KEYALL.GLOBDAT)
         {
         status = ERR_KEYOVL;
         goto end_of_it;
         }
      }
 
   /* everything o.k. - do what's necessary */
 
   KEYALL.GLOBNO ++;
   koffs = KEYALL.GLOBEND + 1;
   KEYALL.GLOBEND += tot_len;
   keypntr = KEYALL.KEYNAMES + KEYALL.GLOBNO;	/* point to new entry */
   keypntr->IDENT[16] = ' ';
   }

 
/* -------------------------- */
/*  now the common section    */
/* -------------------------- */
 
memset((void *)keypntr->IDENT,32,(size_t)KEY_NAMELEN);	/* int 32 = ' ' */
(void) memcpy(keypntr->IDENT,kname,(size_t)kstrn);
keypntr->IDENT[15] = k_typ[0];
keypntr->BYTELEM = (short int) bytelem;
keypntr->NOELEM = noelem;
keypntr->LEN = byt_len;
if (k_typ[0] == 'C')
   keypntr->OFFSET = koffs;
else
   keypntr->OFFSET = (koffs + pad) /sizbyt;
keypntr->FRPAD = (short int) pad;
keypntr->UNIT = 0;		/* (keypntr->UNIT = *unit) not used yet... */
return ERR_NORMAL;

 
/*  That's it folks...  */
 
end_of_it:
MID_ERROR("MIDAS","MID_DEFKEY:",status,0);
return status;
}
 
/*

*/

int MID_DELKEY(name)
 
/*++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE
  delete a user defined keyword
.ALGORITHM
  check input parameters and update pointers
  do not fill the data block
.RETURN
  status:	Return status if = 0, everything is o.k.
--------------------------------------------------*/
 
char   *name;	/* single keyword name,  or  
                   list of keyword names separated by a comma, or  
                   name of file containing keyword names (file.cat) */
 
{
int   status, kentry, n, mm, nval, sta, all;
int   kid=0, kunit;

char  kname[18], k_typ[4], work[40], prombuf[84];



n = CGN_INDEXS(name,".cat");           /* check for keyword catalog */
if (n < 1)
   {
   sta = 0;
   all = 0;
   nval = CGN_COPY(prombuf,name);
   goto extra_key;
   }


/* so we have a catalog */

   all = 2;
   kid = CGN_OPEN(name,READ);
   if (kid < 0) return (ERR_INPINV);

cat_loop:
sta = 0;
nval = osaread(kid,prombuf,80);
if (nval < 1)                            /* EOF - get out */
   {
   osaclose(kid);
   return (ERR_NORMAL);
   }

extra_key:
n = CGN_EXTRSS(prombuf,nval,',',&sta,kname,KEY_NAMELEN);
if (n <= 0)                    /* line finished */
   {
   if (all == 2) goto cat_loop;         /* get next record from catalog */
   return (ERR_NORMAL);
   }
 
 
/*  find keyword entry  */
 
kentry = MID_FNDKEY(kname,k_typ,&mm,&mm,&kunit);
if (kentry <= KEYALL.SYSNO)
   {
   if (kentry < 0)
      {
      (void) snprintf(work,(size_t)40,"keyword %s not found...",kname);
      SCTPUT(work);
      goto extra_key;              /* see, if we can extract more  */
      }
   else 
      {
      status = ERR_KEYBAD;		/* system keys may not be deleted... */
      goto erro_end;
      }
   }

 
keypntr = KEYALL.KEYNAMES + kentry;	 /* everything o.k. - point to entry */
keypntr->IDENT[0] = '\\';			/* mark keyname as deleted */

 
/*   if we delete last key, just change pointers   */

if (kentry == KEYALL.GLOBNO)
   {
   while(keypntr->IDENT[0] == '\\') 		/* may be more to delete... */
      {
      KEYALL.GLOBEND -= (keypntr->LEN + keypntr->FRPAD);
      KEYALL.GLOBNO --;
      keypntr --;
      }
   }

goto extra_key;              /* see, if we can extract more  */

erro_end:
if (all == 2) osaclose(kid);
return status;
}
 
/*

*/

void MID_PACKY()
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE 
  remove delete keywords (header + data space)
.ALGORITHM  
  move through keyword structure + recalculate data offset
  copy header + data into space freed by deleted keyword
.RETURNS  
  nothing.
-------------------------------------------------------*/

{
int globend, tot_len, pad;
int keyno, koffs;
int  sizbyt = 0;
register int nr, mr;

char  tt;
char    *cpntra, *cpntrb;
int     *ipntra, *ipntrb;
float   *fpntra, *fpntrb;
double  *dpntra, *dpntrb;
size_t  *spntra, *spntrb;

struct KEY_STRUCT  *keypntra;

 
/* loop through GLOBNAMES from 0 till current KEYALL.GLOBNO  */
 
keypntr = KEYALL.KEYNAMES;
keyno = -1; globend = -1;

for (nr=0; nr<=KEYALL.GLOBNO; nr++)	/* KEYALL.GLOBNO is last entry used */
   {
   if(keypntr->IDENT[0] == '\\')  
      {
      keypntra = keypntr + 1;
      for (mr=nr+1; mr<=KEYALL.GLOBNO; mr++)
         {					/* search for 1. rela entry */
         if(keypntra->IDENT[0] != '\\')  
            {
            koffs = globend + 1;

            (void) strncpy(keypntr->IDENT,keypntra->IDENT,16);
            keypntr->BYTELEM = keypntra->BYTELEM;
            keypntr->NOELEM = keypntra->NOELEM;
            keypntr->LEN = keypntra->LEN;
            keypntr->UNIT = keypntra->UNIT;

            tt = keypntr->IDENT[15];
            if (tt == 'C')
               {
               keypntr->FRPAD = 0;
               keypntr->OFFSET = koffs;
               tot_len = keypntr->BYTELEM * keypntr->NOELEM;
               cpntra = KCWORDS + keypntr->OFFSET;
               cpntrb = KCWORDS + keypntra->OFFSET;
               CGN_COPYALL(cpntra,cpntrb,tot_len);
               }
            else
               {
               register int kr;

               if (tt == 'I')
                  sizbyt = II_SIZE;
               else if (tt == 'R')
                  sizbyt = RR_SIZE;
               else if (tt == 'D')
                  sizbyt = DD_SIZE;
               else
                  sizbyt = SS_SIZE;

               pad = sizbyt - 1 - (globend % sizbyt);
               keypntr->FRPAD = pad;
               keypntr->OFFSET = (koffs + pad) / sizbyt;
               tot_len = pad + (keypntr->NOELEM * sizbyt);

               if (tt == 'I')
                  {
                  ipntra = KIWORDS + keypntr->OFFSET;
                  ipntrb = KIWORDS + keypntra->OFFSET;
                  for (kr=0; kr<keypntr->NOELEM; kr++)
                     *ipntra++ = *ipntrb++;
                  }
               else if (tt == 'R')
                  {
                  fpntra = KRWORDS + keypntr->OFFSET;
                  fpntrb = KRWORDS + keypntra->OFFSET;
                  for (kr=0; kr<keypntr->NOELEM; kr++)
                     *fpntra++ = *fpntrb++;
                  }
               else if (tt == 'D')
                  {
                  dpntra = KDWORDS + keypntr->OFFSET;
                  dpntrb = KDWORDS + keypntra->OFFSET;
                  for (kr=0; kr<keypntr->NOELEM; kr++)
                     *dpntra++ = *dpntrb++;
                  }
               else
                  {
                  spntra = KSWORDS + keypntr->OFFSET;
                  spntrb = KSWORDS + keypntra->OFFSET;
                  for (kr=0; kr<keypntr->NOELEM; kr++)
                     *spntra++ = *spntrb++;
                  }
               }

            keypntra->IDENT[0] = '\\';	/* mark copied key as deleted */
            goto join;
            }
         keypntra ++;
         }

      goto end_of_it;			/* only deleted keys in the end */
      }

   else
      {					/* calculate last byte of data space */
      tt = keypntr->IDENT[15];
      if (tt == 'C')
         tot_len = keypntr->BYTELEM * keypntr->NOELEM;
      else
         {
         if (tt == 'I')
            sizbyt = II_SIZE;
         else if (tt == 'R')
            sizbyt = RR_SIZE;
         else if (tt == 'D')
            sizbyt = DD_SIZE;
         else
            sizbyt = SS_SIZE;

         if (globend < 0)
            pad = 0;
         else
            pad = sizbyt - 1 -(globend % sizbyt);
         tot_len = pad + (keypntr->NOELEM * sizbyt);
         }
      }

join:
   globend += tot_len;
   keyno ++;
     
   keypntr ++;
   }


end_of_it:
KEYALL.GLOBNO = keyno;
KEYALL.GLOBEND = globend;
}


