/*  ******************************************************************
 *                                                                   *
 * File             : rs.c                                           *
 *                                                                   *
 * Purpose          :                                                *         
 *                                                                   *
 * Version Number   : 1.8                                            *
 *                                                                   *
 * Revision History :                                                *
 *                                                                   *
 * Date                              Developer                       *
 * ----                              ---------                       *
 *  used to establish levels in a flat file                          *
*/

#define RS_VERSION "rs  version 1.8   9 Jul 2004"
/*
 *  9 Jul 04                          wjs                            *
 *	Typo fix						     *
 *	Add iovaldouble_ entry					     *
 * 23 Apr 04                          wjs                            *
 *	Mods to "version-returning function".			     *
 *	  a) change from local to global to ensure it can't be       *
 *	     "optimized out"					     *
 *	  b) don't want "version" in any function name since such    *
 * 	     names appear when grep'ping for "version"		     *
 * 18 Feb 04                          wjs                            *
 *	Error if variable in levelizing list is not in object being  *
 *	  levelized						     *
 *	Check some potential buffer overflows			     *
 *	#include core.h.  Allows us to throw out stuff, and also     *
 *	  gets rid of some compiler warnings			     *
 *	"Full" error messages					     *
 *	    [Needs utils; path_info_routines]			     *
 *	    [Begin 1.8]						     *
 * 25 Jul 03                          wjs                            *
 *	Dynamically allocate comment buffer size		     *
 *	    [Needs path_info_routines 1.1 or later (.c & .h)]	     *
 *	    [Begin 1.7]						     *
 * 28 Jan 99                          wjs                            *
 *	Use path_info_routines to control PATH_INFO; change          *
 *	  PATH_INFO just before needed; change it back when done     *
 *	Add version string; reorder comments	                     *
 *	    [Needs path_info_routines 1.1 or later (.c & .h)]	     *
 *	    [Begin 1.6]						     *
 * June 1997 grf                                                     *
 *       changed sub names to in_xxxx, fixes for v 1.5               *
 * May 1997                         Glenn Flierl                     *
 *       changed error calls to use error_ in outer.c                *
 * May 21, 1996                       clh                            *
 *  iovalreal change to allow alpha vars to begin with digits        *
 * March 1996                         clh                            *
 *  use minbufsize.h to determine size of buffers                    *
 * January 1996                       clh                            *
 *  fixed rsw to respect the re-ordering of variables                *
 * July 1995                          clh                            *
 *  add subroutine to return column widths - iowidth_()              *
    ******************************************************************
*/
#include INNEROPTIONS
#include "path_info_routines.h"

double strtod();

/* #define DEBUG */

int nlevels,nnames;
int firstvar[6];
char names[NVAR][VARNAMESIZE];
int fldwidths[NVAR];
int namesize=VARNAMESIZE;
char values[NVAR][DATUMSIZE];
char comp[NVAR][DATUMSIZE];
int valuesize=DATUMSIZE;
int handle;
int minlevelread;
char *comments;

char ss[NVAR][VARNAMESIZE+3];	/* 1 for level; 1 for :; 1 for \0	*/
int ns=0;
int newlev[NVAR];
int newpntr[NVAR];
int newlpntr[11];
int maxoutlev;

Logical add_id_to_err();	/* in utils.c				*/
void error_();			/* in outer				*/

/************************************************************************/

char *rs_return_vers()
/*  Dummy routine.  Exists only to force .h file version string into	*/
/*  this module.  Note string must not be global or we'll have con-	*/
/*  flicts if another routine similarly includes the version string	*/
{
  static char version[] = RS_VERSION"/"FULL_PATH_INFO_ROUTINESH_VERSION;
  return version;
}

void err(s,t)
char *s,*t;
{
  char *ss,*tt;
  add_id_to_err(&ss,&tt,s,t,RS_VERSION);
  error_(ss,tt);
  return;		/*  Not that it should ever get here...		*/
}

int iovarlevel_(vn)
int *vn;
{
  return newlev[newpntr[*vn]];
}

void in_getwidth(vn, ptr)
/* read the width from the string 'ptr' (an attribute that has
        been tested prior to coming here for containing the 
        string 'width=') and write it to the fldwidths array
        element '*vn'
*/
int *vn;
char *ptr;
{
  char *p;
  int len;
  p = strchr(ptr,'=');

  sscanf(p+1,"%d", &len);

  fldwidths[*vn] = len;
  return;
}

int ioattrout_(vn,str)
int *vn;
char *str;
{
   int j;

   j = newpntr[*vn];

   if ((jdbattributes_(&handle,&j,str)) == 0)
       return 0;
   else {
       if (strncmp(str,"width=",6) == 0) {
         in_getwidth(&j,str);
       }      
       return 1;
   }
}

void iovaldouble_(vn,df)
int *vn;
double *df;
/*	See iovalreal_							*/
{
/* following line changed by clh, May 1996
if(strspn(values[i],"0123456789.+-"))
  to one below, to allow for strings that begin with digits
*/

  int i;
  char *end_char_ptr;

  i=newpntr[*vn];
  if(i<0) {*df=-9999.0; return;};

/* following line changed by clh, May 1996
  if(strspn(values[i],"0123456789.+-"))
  to one below, to allow for strings that begin with digits
*/

  *df=strtod(values[i],&end_char_ptr);
  if (*end_char_ptr != '\0')
   *df= -9999.0;

return;
}

void iovalreal_(vn,f)
int *vn;
float *f;
/*	"Return real value (f) for variable indexed by vn. -9999"	*/
/*	"for strings"							*/
{
  double df;
  iovaldouble_(*vn,&df);
  *f = df;
  return;
 }

void iovalstr_(vn,tmp)
int *vn;
char *tmp;
{
  char *s;

  s=values[newpntr[*vn]];
  s=s+strspn(s," ");
  strcpy(tmp,s);
}

void ioname_(vn,s)
int *vn;
char *s;
{
  strcpy(s,names[newpntr[*vn]]);
}

int iocommout_(str)
char *str;
{
char *at;

if (comments[0]){
  at=strchr(comments,'\n');
  if(at){
    *at = '\0';
    strcpy(str,comments);
    strcpy(comments,at+1);
  } else {
    strcpy(str,comments);
    comments[0]='\0';
  };
  return 1;
} else return 0;
}

int iowidth_(vn)
int *vn;
{
   int i;

   i = fldwidths[newpntr[*vn]];
return i;
}

int ioreadrec_(level)
int *level;
{
  int clevel,i;

  if(*level == minlevelread)
    {
      minlevelread= *level+1;
      return 1;
    }
  else if(*level >minlevelread) return 0;

  while(1){
    clevel=jdbreada_(&handle,values,&valuesize);
    /* printf("clevel %d\n",clevel); */
    if(clevel<0){
      minlevelread= -1;
      return 0;
    };
    
    for(i=firstvar[clevel];i<nnames;i++){
      if(newlev[i]<minlevelread)
	if(strcmp(values[i],comp[i]))minlevelread=newlev[i];
      COPY_INTO_FIXED_LEN_BUFFER(comp[i],values[i],"copying into comp buffer");
    };
    
    if (minlevelread < *level) return 0;
    else if(minlevelread == *level){
      minlevelread++;
      return 1;
    };
  };
}

void ioclose_()
{
  jdbclose_(&handle);
}

int ioopen_(s,nparams,ntotal)
char *s[];
int *nparams;
int *ntotal;
{
  char tmp[INBUFSIZE],*sp;
  int i,j,k,m,maxclev;
  int position_in_comments,size_comments;

    /*  PATH_INFO function and strings.					*/
  char *make_PATH_INFO_putenv_string();
      /* "PATH_INFO=" + getenv("PATH_INFO").  Must be static		*/
      /*  since it "survives" in process table after ioopen_ exits)	*/
  static char *PATH_INFO_orig_putenv;
      /* "PATH_INFO=" + getenv("PATH_INFO") w/ protocol = "jgof"	*/
  char *PATH_INFO_jgof_putenv;

    /*  Must be sure that methods down the line produce JGOFS	*/
    /*  protocol (or whatever jdbopen requires), and not html,	*/
    /*  flat, or whatever.  Do this by replacing proto string	*/
    /*  with "jgof" (WJS decision at this point... jgof has no	*/
    /*  meaning to system).					*/
    /*    Make 2 strings for putenv.  1 sets up existing 	*/
    /*    PATH_INFO; the other sets up PATH_INFO with "jgof"	*/
    /*    protocol						*/  
    /*    PATH_INFO_ENV_VAR & USE_EXISTING_LEVEL defined in	*/
    /*    path_info_routines.h					*/
  if (getenv(PATH_INFO_ENV_VAR) == NULL) {
    PATH_INFO_orig_putenv = NULL;
    PATH_INFO_jgof_putenv = NULL;
  } else {
    PATH_INFO_orig_putenv = 
      make_PATH_INFO_putenv_string(NULL,NULL,NULL,NULL,USE_EXISTING_LEVEL,NULL);
    if (PATH_INFO_orig_putenv == NULL)
      err ("Cannot make PATH_INFO_orig_putenv",
	      "Memory problems or PATH_INFO not in expected format");
    PATH_INFO_jgof_putenv = 
      make_PATH_INFO_putenv_string
				(NULL,NULL,NULL,"jgof",USE_EXISTING_LEVEL,NULL);
    if (PATH_INFO_jgof_putenv == NULL)
      err ("Cannot make PATH_INFO_jgof_putenv",
	      "Memory problems or PATH_INFO not in expected format");
  }

  if (PATH_INFO_jgof_putenv != NULL) 
    if (  (i = putenv(PATH_INFO_jgof_putenv)) != 0  )
      err ("putenv failure for PATH_INFO",strerror(i));

  COPY_INTO_FIXED_LEN_BUFFER(tmp,s[0],"copying object name");

  nnames = -NVAR;
  maxclev=jdbopen_(&handle,tmp,names,&namesize,&nnames);

  if (maxclev<0){err("Bad Object returned from jdbopen ",s[0]);};

    /*  Restore original PATH_INFO if we changed it		*/
  if (PATH_INFO_jgof_putenv != NULL) {
    if (  (i = putenv(PATH_INFO_orig_putenv)) != 0  )
       err ("putenv failure restoring PATH_INFO",strerror(i));
    free(PATH_INFO_jgof_putenv);
  }

  s[0][0]=0;
  comments = NULL;
  size_comments = 0;
  position_in_comments = 0;
    /*  Note that jdbcomments could overflow tmp - there is no consis-	*/
    /*  tency check between the buffer in jdbcomments and the parameter	*/
    /*  we use to size tmp						*/
    /*  Note we could jigger this to have jdbcomments_ put stuff di-	*/
    /*  rectly in comments, too						*/
  while (jdbcomments_(&handle,tmp)) {
    i = strlen(tmp);
      /*  +2 because we need a \n per tmp as well as a final \0		*/
    if (size_comments < position_in_comments + i + 2) {
	/*  COMMENTSIZE in next line could be "anything"		*/
      size_comments += (i+2 > COMMENTSIZE) ? i+2 : COMMENTSIZE;
      comments = (char *)realloc(comments,size_comments);
      if (comments == NULL)
	err("Could not allocate space for comments buffer","");
    }
    strncpy (comments + position_in_comments, tmp, i);
    position_in_comments += i;
    *(comments + position_in_comments++) = '\n';
    *(comments + position_in_comments) = '\0';
  }

  for(i=1;i<*nparams;i++)if(s[i][0]>='0' && s[i][0]<='9' && s[i][1]==':'){
    COPY_INTO_FIXED_LEN_BUFFER(ss[ns++],s[i],"copying levelization parameter");
    s[i][0]='\0';
  };
  
  j=0;
  firstvar[j]=0;
  for(i=0;i<nnames;i++){
    newlev[i]= -1;
    comp[i][0]=0;
    if((k=iovarlevel_(&handle,&i))>j)
      firstvar[++j]=i;
  };

  maxoutlev= -1;
  m=0;
  for(k=0;k<ns;k++){
    COPY_INTO_FIXED_LEN_BUFFER(tmp,ss[k],"copying levelization parameter into tmp");
    i=tmp[0]-'0';
    if(i>maxoutlev){maxoutlev=i;newlpntr[maxoutlev]=m;};
    if(tmp[2]=='*'){
      for(j=0;j<nnames;j++)if(newlev[j]== -1){
	newlev[j]=maxoutlev;
	newpntr[m++]=j;
      };
    } else {
      for(j=0;j<nnames;j++)
	if(strcmp(names[j],tmp+2)==0)break;
      if(j<nnames){
	newlev[j]=maxoutlev;
	newpntr[m++]=j;
      } else 
	err("Variable in levelization list not in object.  Variable=",tmp+2);
    };
  };
  
  if(maxoutlev<0)maxoutlev=0;
  
  for(j=0;j<nnames;j++)
    if(newlev[j]<0){
      newlev[j]=maxoutlev;
      newpntr[m++]=j;
    };
  
  *ntotal=nnames;

  minlevelread = maxoutlev+1;
  return maxoutlev;
}
