/* jdb.c  - forms primary routines for the JGOFS data system		*/
/*              used from jgofs.a					*/

#define JDB_VERSION "jdb version 1.1g  25 Aug 2004"

/*  25 Aug 04.  1.1g    WJS						*/
/*	Use core.h							*/
/*	Standardize version stuff					*/
/*	Get rid of strdup (ULTRIX) function				*/
/*  17 Jan 04.  1.1f    WJS						*/
/*	localopen was parsing "par" starting from par+1, missing	*/
/*    initial (.  Seems this was an oversight - compare w/serv.c	*/
/*  18 Dec 03.  1.1e    WJS						*/
/*	Get rid of last(?) 3 exits (in favor of "return error"s)	*/
/*  29 Jul 03.  1.1d    WJS						*/
/*	JDB_DEBUGn conditional compilations				*/
/*	    n=1 means print something to stdout for all non-0 returns	*/
/*	      2 means enable original debug traces			*/
/*	      3 means dump data buffers					*/
/*  25 Jun 03.  1.1d    WJS						*/
/*	Get rid of extra "opening" in error message			*/
/*  15 Nov 01.  1.1c    WJS						*/
/*	'Bout time to fix last year's bug discoveries			*/
/*   4 Nov 00.  1.1c    WJS						*/
/*      Put in a couple of #### ERROR #### comments to point to probs	*/
/*    that should be fixed.  No fixes yet				*/
/*   5 May 00.  1.1b    WJS						*/
/*	"major" and "minor" variable names caused trouble on Mike Car-	*/
/*    uso's Solaris 7 box.						*/
/*   1 Feb 00.  1.1a    WJS						*/
/*	Bug fix: HTDoRead can return a negative value.  Handle this.	*/
/*	Bug fix: -998 comment from 1 Oct not fully implemented. Do it.	*/
/*  18 Oct 99.  1.1    WJS						*/
/*	Skip data before initial &.  Original code assumed jdbopen be-	*/
/*    gan in "&c" mode.  Addition of HTTP/1.0 to GET causes server to	*/
/*    respond w/various info (terminated by empty crlf line) before	*/
/*    beginning w/real data						*/
/*	Do some restructuring... but not enough.			*/
/*   1 Oct 99.  1.1    WJS						*/
/*      Add "Host: " request-header field to GET's, along with HTTP/1.0	*/
/*    ID.  Host: required to implement virtual hosting.  ID seems to be	*/
/*    required in order to make Host: effective... ?  Content of mods	*/
/*    from rfc 2616 and from Glenn's "last" transfer.c code, which ad-	*/
/*    dressed this issue ~Dec 97.					*/
/*	Add some buffer size tests in this part of the code.  Return	*/
/*    -998 if there would be overflow.  There might be perverse code	*/
/*    that thinks "only -999 means bad".  -998 will take the "OK" path	*/
/*    and die in an ugly fashion.  On the other hand, w/o the buffer    */
/*    testing, a failure would have been ugly whether it happened be-	*/
/*    fore or after return from jdb					*/
/*	Define macros to make it compile more cleanly (under VMS anyway)*/
/*	[Begin 1.1]							*/
/*									*/
/*  08 Sep 99.  1.0b.  CLH						*/
/*	Resolve differences from 2 sources - v1.0a and OO version  	*/
/*									*/
/*  15 Aug 99.  1.0a.  WJS						*/
/*    Looks to me like there is an "open file leak" in localopen/	*/
/*    jdbclose interaction.  We open a pipe to the child and one from	*/
/*    it, but only close the one from it.  Try closing the pipe to it	*/
/*    right from the start - don't see that we should be writing to	*/
/*    child...								*/
/*	[Begin 1.0a]							*/
/*  November 3, 1998, clh  system version 1.5 upgrade			*/
/*  November 10, 1998, clh several functions need to be 'typed'	 	*/

#include "core.h"

#ifndef JDB_DEBUG1
#define JDB_DEBUG1 0
#endif
#ifndef JDB_DEBUG2
#define JDB_DEBUG2 0
#endif
#ifndef JDB_DEBUG3
#define JDB_DEBUG3 0
#endif
#if JDB_DEBUG1
#define CHAR_DEBUG1 "DEBUG1 "
#else
#define CHAR_DEBUG1 ""
#endif
#if JDB_DEBUG2
#define CHAR_DEBUG2 "DEBUG2 "
#else
#define CHAR_DEBUG2 ""
#endif
#if JDB_DEBUG3
#define CHAR_DEBUG3 "DEBUG3 "
#else
#define CHAR_DEBUG3 ""
#endif
#if JDB_DEBUG1 || JDB_DEBUG2 || JDB_DEBUG3
#define NO_DEBUG "D"
#else
#define NO_DEBUG "No d"
#endif
  char *debug_flags=NO_DEBUG "ebug flags " CHAR_DEBUG1 CHAR_DEBUG2 CHAR_DEBUG3;

#if SOL || HP
#include <sys/time.h>
#include <sys/resource.h>
#endif

/* added for JGOFS release v1.5 compatibility (not clear which parts of	*/
/*   these OPTIONS & INNEROPTIONS are actually required - WJS)		*/
#include INNEROPTIONS

int dctsearch();

int HTDoConnect();
int HTDoRead();

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

char *jdb_return_vers()
/*  Routine exists mostly to force .h file version string into this	*/
/*  module, but we could call it if we want.  Note string must not be	*/
/*  global or we'll have conflicts if another routine similarly		*/
/*  includes the version string						*/
{
  static char version[] = JDB_VERSION"/"COREH_VERSION;
  return version;
}

HTCheckActiveIcon(i)
int i;
{
return 0;
}

void HTClearActiveIcon()
{
}

void HTProgress(msg)
char *msg;
{
/*
fprintf(stderr,"%s\n",msg);
*/
}


/* below values indicating buffers should be minimally 4k */
#define HTSIZE INBUFSIZE
#define MAX_BUF INBUFSIZE
#define NUMNAMES NVAR

#define MAX_UNITS 5

struct OBJECT {
 char htbuff[HTSIZE];
 int htbufflen;
 int htbuffptr;
 int socket;
 int maxlevel,ntotal;
 int lvlpntr[11];
 int rdpntr[NUMNAMES];
 char attributes[NUMNAMES][TOTATTRSIZE], *attributeptr[NUMNAMES];
 char comments[COMMENTSIZE], *commentptr;
}  *object[MAX_UNITS]={NULL,NULL,NULL,NULL,NULL};

int bgets(comm,len,obj)
char *comm;
int len;
struct OBJECT *obj;

{
  int op;
  char c;
#if JDB_DEBUG2
  printf("entered bgets\n");
#endif

if (obj->htbufflen < 0) {
#if JDB_DEBUG1
  printf ("Immediate exit from bgets - obj->bufflen = %d on entry\n",
		obj->htbufflen);
#endif
  return 1;
}

op = 0;
while (1) {
  if (obj->htbuffptr >= obj->htbufflen) {
    obj->htbufflen = HTDoRead(obj->socket,obj->htbuff,HTSIZE);
    if (obj->htbufflen <= 0) {
#if JDB_DEBUG1
      printf("Exit from bgets due to non-pos return (%d) from HTDoRead\n",
		obj->htbufflen);
#endif
      obj->htbufflen= -1;
      return 1;
    }
    obj->htbuffptr = 0;
#if JDB_DEBUG3
    printf("<pre>DATA!!!%s!!!ENDDATA\n</pre>",obj->htbuff);
#endif
  }

    /*  Note: RFC 1945 says that "bare" LF or CR (as well as CRLF)	*/
    /*  should be accepted.  Code below doesn't accept bare CR		*/
  if (  (c=obj->htbuff[obj->htbuffptr++]) != '\r'  ) {
    if (op < len) comm[op++] = c;
    else {
      comm[len] = '\0';		/* For sanity's sake...			*/
      obj->htbufflen= -1;
#if JDB_DEBUG1
      printf("Exit from bgets due to buffer ovf.  buf siz = %d\n",
		len);
#endif
      return -1;
    }
  }
  if (c == '\n') {
    comm[--op] = '\0';
    return 0;
  }
}
}  
  
int htopen(comm,par,obj)
char *comm,*par;
struct OBJECT *obj;
{
  char *cp,met[MAX_BUF];
  int i,handle;
  int reqd_htbuff_size;

  cp=strchr(comm+2,'/');
  if (cp==NULL) {
#if JDB_DEBUG1
    printf ("Exit from htopen - expected / missing.  buffer = %s\n",comm);
#endif
    return -999;
  }
  *cp =0;

    /*  13 for GET /jg/serv/; 9 for " HTTP/1.0"; 3 for cr,lf,null	*/
    /*  at end								*/
  reqd_htbuff_size = 13 + strlen(cp+1) + 9 + 3;
  if (reqd_htbuff_size > sizeof(obj->htbuff)) return -998;
  strcpy(obj->htbuff,"GET /jg/serv/");
  strcat(obj->htbuff,cp+1);
  if(par){
    if(*par){
	/*  1 for ?							*/
      reqd_htbuff_size += 1 + strlen(par);
      if (reqd_htbuff_size > sizeof(obj->htbuff)) return -998;
      strcat(obj->htbuff,"?");
      strcat(obj->htbuff,par);
    };
  };
  strcat(obj->htbuff," HTTP/1.0");

    /* 5 for http:							*/
  if ((5 + strlen(comm)) > sizeof(met)) return -998;
  strcpy(met,"http:");
  strcat(met,comm);
  i=HTDoConnect(met,"HTTP",80,&handle);
  if (i != 0) {
    printf("Connect to %s failed\nHTDoConnect return = %d\n",met,i);
    return -999;
  }
  obj->socket = handle;

  cp=obj->htbuff+strlen(obj->htbuff);
  *(cp++)='\r';
  *(cp++)='\n';
  *cp=0;
    /*  6 for "Host: "; 4 for crs,lfs.  Don't need to count null since	*/
    /*  "old" null, above, is being overwritten by stuff		*/
  reqd_htbuff_size += 6 + strlen(comm+2) + 4;
  if (reqd_htbuff_size > sizeof(obj->htbuff)) return -998;
  strcat(obj->htbuff,"Host: ");
  strcat(obj->htbuff,comm+2);
  cp = obj->htbuff + reqd_htbuff_size - 5; /* ... + strlen(obj->htbuff)	*/
  *(cp++)='\r';
  *(cp++)='\n';
  *(cp++)='\r';
  *(cp++)='\n';
  *cp=0;

    /*  -1 so null is not included					*/
  write(handle,obj->htbuff,reqd_htbuff_size-1);
  return 0;
}

int localopen(comm,par,obj)
char *comm,*par;
struct OBJECT *obj;
{
  char *cp,met[MAX_BUF];
  int i,handle;
  FILE *cin,*cout;
  static int pipeto[2],pipefrom[2];
  char *parptr[250],*sp;
  int j,np,childpid,numfds;
#if SOL || HP
  struct rlimit rlp;
#endif
  j=0;
  parptr[j]=comm;
  if(*par){
    parptr[++j]=par;
    np=0;
    sp=par;
    while(*sp){
      switch(*sp){
      case '(':np++;break;
      case ')':np--;break;
      case ',':if(np)break;
	*sp=0;
	parptr[++j]=sp+1;
	break;
      };
      sp++;
    };
  };
  parptr[++j]=NULL;
  
  if (pipe(pipeto) || pipe(pipefrom) <0)
    {
      perror("no pipes");
      return -995;
    }
  switch(childpid = fork())
    {
    case -1:
      perror("bad fork");
      return -994;
    case 0:
#if JDB_DEBUG2
      printf("got here after fork\n");
#endif
      dup2(pipeto[0],0);
      dup2(pipefrom[1],1);
#if SOL || HP
      getrlimit(RLIMIT_NOFILE,&rlp);
      numfds=rlp.rlim_cur;
#else
      numfds = getdtablesize();
#endif
      for (i=3; i<numfds; i++)
	close(i);
      
      execvp(comm,parptr);
      perror(comm);
      return -999;
    default:
      close(pipeto[0]);
      close(pipefrom[1]);
      /*    ptoch = pipeto[1];*/
      /*    pfrch = pipefrom[0];*/
      obj->socket=pipefrom[0];
      close(pipeto[1]);			/*  wjs addition 15 Aug 99	*/
      /*    setbuf(frch,NULL);*/
      return 0;
    }
  
}

void close_object(tunit)
int tunit;
{
  close (object[tunit]->socket);
  free (object[tunit]);
  object[tunit] = NULL;
  return;
}

int skip_one_line_from_outer(comm,tunit)
int tunit;
char *comm;
{
  switch (bgets(comm,MAX_BUF-1,object[tunit])) {
  case -1:
    printf("&x <b>jdb: data line too big.  Start of line:</b> %s\n",comm);
    return -996;
  case 1:
#if JDB_DEBUG1
    printf ("skip_one_line exiting due to getting a 1 from bgets\n");
#endif
    return -999;
  }
  if (comm[0]=='&')
    if (comm[1]=='x') {
      printf("%s\n",comm);
      close_object(tunit);
      return -999;
    }
  return 0;
}

#if IBM || HP
int jdbopen(unit,obj,names,namesize,num)
#else
int jdbopen_(unit,obj,names,namesize,num)
#endif
int *unit;
char *obj;
char *names;
int *namesize;
int *num;
{
  int i,j,k,numd,tunit;
  int remote_object_flag,nitem;
  int http_vers_major,http_vers_minor,http_status,http_text_offset;
  char comm[MAX_BUF];
  char met[MAX_SERV_DCT_BUF],par[MAX_SERV_DCT_BUF];
  char state,*cp,*ap;
  struct OBJECT *o;

  for (tunit = 0; tunit < MAX_UNITS; tunit++)
    if (object[tunit] == NULL) break;
  if (tunit >= MAX_UNITS) {
    printf("Out of units\n");
    return -999;
  }
  o = (struct OBJECT *)malloc(sizeof(struct OBJECT));
  object[tunit]=o;
  *unit = tunit;

  strcpy(comm,obj);
  numd = *num;
  if (*num <0 ) *num=0;
  else {
    if (comm[strlen(comm)-1] == ')') comm[strlen(comm)-1] = ',';
    else strcat(comm,"(");
    for (i = 0; i < numd; i++) {
      strcat(comm,names+i*(*namesize));
      strcat(comm,",");
    }
    comm[strlen(comm)-1]=')';
  }
#if JDB_DEBUG2
  printf("++%s++\n",comm);
#endif

/* find it */
  if (cp = strchr(comm,'(')) {
    strcpy(par,cp+1);
     *cp=0;
     par[strlen(par)-1]=0;
  } else *par=0;
  if (dctsearch(comm,met,par,1) == 0) {
    printf("&x <b>Error - not found:</b> %s %s\n",met,comm);
    return -993;
  }

  remote_object_flag = (strstr(met,"//") == met);
  if (remote_object_flag) {
    if (  (i = htopen(met,par,o)) != 0 ) {
      cp = (i == -998) ? "htopen buffer overflow" : "Error";
      printf("&x <b> %s opening remote object:</b> %s\n",cp,met);
      return i;
    }
  } else
    if (localopen(met,par,o) != 0) {
      printf("&x <b>Error opening local object:</b> %s\n",met);
      return -999;
    }

  o->htbufflen=0;
  o->htbuffptr=0;
  *(o->comments)='\0'; 
  o->commentptr = o->comments;


  for(i=0;i<NUMNAMES;i++){
    o->rdpntr[i]= -1;
    o->attributeptr[i] = o->attributes[i];
  };
  o->ntotal=0;
  
  if (remote_object_flag) {
      /*  We sent a "full request".  Check that we got a "full		*/
      /*  response" (eg, "HTTP/1.0 200 OK")... and (mostly) skip it.	*/
      /*  Details in RFC 1945.  Note in particular that (as I read it)	*/
      /*  the space between the "200" and the "OK" is required.  Also	*/
      /*  note that we allow more than one blank around status which	*/
      /*  may not be standard-compliant					*/
    switch (bgets(comm,MAX_BUF-1,o)) {
    case -1:
       printf("&x <b>jdb: data line too big.  Start of line:</b> %s\n",comm);
       return -996;
    case 1:
#if JDB_DEBUG1
       printf("jdbopen (remote obj) exiting due to return of 1 from bgets#1\n");
#endif
       return -999;
    }
    nitem = sscanf(comm,"HTTP/%d.%d %d %n",
	&http_vers_major,&http_vers_minor,&http_status,&http_text_offset);
    if (nitem < 3) {
      printf ("jdb: remote http server not >= v 1.0? response: %s\n",comm);
      close_object(tunit);
      return -999;
    }
      /*  Maybe should do better job w/other statuses			*/
    if (http_status >= 400) {
      printf ("jdb: http protocol error %d text: %s\n",
					http_status, comm + http_text_offset);
      close_object(tunit);
      return -999;
    }
    while (comm[0] != '\0') {
      if (bgets(comm,MAX_BUF-1,o) == 1) {
#if JDB_DEBUG1
       printf("jdbopen (remote obj) exiting due to return of 1 from bgets#2\n");
#endif
       return -999;
      }
    }
  } else {
      /*  Skip "content-type:text/html"; crlf lines			*/
    i = skip_one_line_from_outer(comm,tunit);
    if (i != 0) return i;
    i = skip_one_line_from_outer(comm,tunit);
    if (i != 0) return i;
  }

  state='c';
  o->maxlevel= 0;
  while(1) {
    switch (bgets(comm,MAX_BUF-1,o)) {
    case -1:
       printf("&x <b>jdb: data line too big.  Start of line:</b> %s\n",comm);
       return -996;
    case 1:
#if JDB_DEBUG1
       printf("jdbopen exiting due to return of 1 from bgets#3\n");
#endif
       return -999;
    }
    if(comm[0]=='&') state=comm[1];
    switch(state){
    case 'x':
      printf("%s\n",comm);
      close_object(tunit);
      return -999;
    case 'e':
#if JDB_DEBUG1 || JDB_DEBUG2
      printf("jdbopen exiting upon receipt of premature eod: %s\n",comm);
#endif
      close_object(tunit);
      return -999;
    case 'c':
      if (comm[0] != '&') {
	strcat(o->comments,comm);
	strcat(o->comments,"\n");
      }
      break;
    case 'r':
      if (numd > 0)
        for (i = 0; i < numd; i++) {
          for (j = 0; j < o->ntotal; j++)
	    if (o->rdpntr[j] == i) break;
          if (j == o->ntotal) {
	    close_object(tunit);
            return -(i+1);
          }
        }
      o->lvlpntr[o->maxlevel+1] = o->ntotal;
      return o->maxlevel;
    case 'v':
      if (comm[0] == '&'){
	o->maxlevel = comm[2]-'0';
	o->lvlpntr[o->maxlevel] = o->ntotal;
      } else {
	cp=strtok(comm,"\t\n");
	while(cp){
	  ap=strchr(cp,'[');
	  if(ap) {
	    i = strlen(ap)-1;
	    if (ap[i] == ']') {
	      ap[0] = '\0';
	      ap[i] = ';';
	      ap++;
	    } else {
	      printf("&x <b>Malformed attribute list (missing ]):</b> %s\n",ap);
	      return -997;
	    }
	  };
	  o->ntotal++;
	  if(numd < 0){
	    (*num)++;
	    if(*num > -numd){
	      printf("&x <b>Too many variables (max = %d)</b>\n",-numd);
	      close_object(tunit);
	      return -999;
	    };
	    strcpy(names+(*namesize)*(*num-1),cp);
	    if(ap) strcpy(o->attributes[*num-1],ap);
	    k= *num -1;
	  } else {
	    k = -1;
	    for (i = 0; i < *num; i++) 
	      if (strcmp(names+(*namesize)*i,cp) == 0) {
		k = i;
		if (ap) strcpy(o->attributes[i],ap);
	      }
	  }
	  if (k >= 0) o->rdpntr[o->ntotal-1] = k;
	  cp = strtok(NULL,"\t\n");
	}
      }
    }
  }
}

#if IBM || HP
int jdbread(unit,values)
#else
int jdbread_(unit,values)
#endif
int *unit;
float values[];
{
  char comm[MAX_BUF],state,*cp;
  int i,k,ilvl,iret;
  struct OBJECT *o;
  if(!(o=object[*unit]))return -1;
  ilvl=o->maxlevel;
  iret=ilvl;
  state='d';
  while(1) {
#if JDB_DEBUG2
    printf(">>%c>>%s\n",state,comm);
#endif
    switch (bgets(comm,MAX_BUF-1,o)) {
    case -1:
       printf("&x <b>jdb: data line too big.  Start of line:</b> %s\n",comm);
       return -996;
    case 1:
#if JDB_DEBUG1
       printf("jdbread exiting due to return of 1 from bgets#4\n");
#endif
       return -999;
    }

    if(comm[0]=='&') {
      state=comm[1];
      switch(state){
      case 'x':
	printf("%s\n",comm);
	close_object(*unit);
	return -999;
      case 'e':
	close_object(*unit);
	return -1;
      case 'd':
        ilvl = comm[2]-'0';
	if (ilvl < iret) iret = ilvl;
	break;
      case 'c':
	*(o->comments)='\0';
	o->commentptr = o->comments;
      };
    } else {
      switch(state){
      case 'c':
	strcat(o->comments,comm);
	strcat(o->comments,"\n");
	break;
      case 'd':
	cp=strtok(comm,"\t\n");
	for(i=o->lvlpntr[ilvl];i<o->lvlpntr[ilvl+1];i++){
	  k=o->rdpntr[i];
	  if(cp && k>=0){
	    if(strspn(cp,"0123456789.+-")==0) values[k]= -9999.99;
	    else values[k]=atof(cp);
	    cp=strtok(NULL,"\t\n");
	  } else if(cp) cp=strtok(NULL,"\t\n");
	  else if(k>=0) values[k]= -9999.99;
	};
	if(ilvl == o->maxlevel)return iret;
      };
    };
  };
}


#if IBM || HP
int jdbreada(unit,values,valuesize)
#else
int jdbreada_(unit,values,valuesize)
#endif
int *unit;
char *values;
int *valuesize;
{
  char comm[MAX_BUF],state,*cp;
  int i,k,ilvl,iret;
  struct OBJECT *o;

  if(!(o=object[*unit]))return -1;
  ilvl=o->maxlevel;
  iret=ilvl;
  state='d';
  while(1) {
#if JDB_DEBUG2
    printf(">>%c>>%s\n",state,comm);
#endif
    switch (bgets(comm,MAX_BUF-1,o)) {
    case -1:
       printf("&x <b>jdb: data line too big.  Start of line:</b> %s\n",comm);
       return -996;
    case 1:
#if JDB_DEBUG1
       printf("jdbreada exiting due to return of 1 from bgets#5\n");
#endif
       return -999;
    }

    if(comm[0]=='&') {
      state=comm[1];
      switch(state){
      case 'x':
	printf("%s\n",comm);
	close_object(*unit);
	return -999;
      case 'e':
	close_object(*unit);
	return -1;
      case 'd':
        ilvl=comm[2]-'0';
	if(ilvl<iret)iret=ilvl;
	break;
      case 'c':
	*(o->comments)='\0';
	o->commentptr = o->comments;
      };
    } else {
      switch(state){
      case 'c':
	strcat(o->comments,comm);
	strcat(o->comments,"\n");
	break;
      case 'd':
	cp=strtok(comm,"\t\n");
	for(i=o->lvlpntr[ilvl];i<o->lvlpntr[ilvl+1];i++){
	  k=o->rdpntr[i];
	  if(cp && k>=0){
	    strcpy(values+(*valuesize)*k,cp);
	    cp=strtok(NULL,"\t\n");}
	  else if(cp) cp=strtok(NULL,"\t\n");
	  else if(k>=0)strcpy(values+(*valuesize)*k,"nd");
	};
	if(ilvl == o->maxlevel)return iret;
      };
    };
  };
}

#if IBM || HP
int jdbcomments(unit,outcom)
#else
int jdbcomments_(unit,outcom)
#endif
int *unit;
char *outcom;
{
  int j;
  struct OBJECT *o;

  if(!(o=object[*unit]))return 0;
  if(*(o->commentptr)){
    j=strcspn(o->commentptr,"\n");
    strncpy(outcom,o->commentptr,j);
    *(outcom+j)='\0';
    o->commentptr += j+1;
    return 1;
  } else return 0;
}

#if IBM || HP
int jdbattributes(unit,id,outcom)
#else
int jdbattributes_(unit,id,outcom)
#endif
int *unit;
int *id;
char *outcom;
{
   int j;
   char *sp;
   struct OBJECT *o;

  if(!(o=object[*unit]))return 0;
  sp=o->attributeptr[*id];
  if(*sp){
  j=strcspn(sp,";");
  strncpy(outcom,sp,j);
  *(outcom+j)='\0';
  o->attributeptr[*id] += j+1;
  return 1;
  } else return 0;
}

#if IBM || HP
int jdbclose(unit)
#else
int jdbclose_(unit)
#endif
int *unit;
{
  struct OBJECT *o;
  
  if ( (o = object[*unit]) == NULL ) return 1;
  close_object(*unit);
  return 0;
}

#if IBM || HP
int jdblevel(unit,varnum)
#else
int jdblevel_(unit,varnum)
#endif
int *unit;
int *varnum;
{
  int i,j;
  struct OBJECT *o;
  
  if(!(o=object[*unit]))return -999;
  for(j=0;j<o->ntotal;j++)if(*varnum==o->rdpntr[j])break;
  if(j>=o->ntotal)return -1;
  for(i=1;i<=o->maxlevel;i++)if(j<o->lvlpntr[i])return i-1;
  return o->maxlevel;
}

/*
char names[20][TOKEN];
char values[20][TOKEN];
int maxlev;

main(argc,argv)
int argc;
char *argv[];
{
char objname[256];
int i,j,clev;
int unit=1;
int nn;
int namesize=TOKEN;
int valuesize=TOKEN;

nn= -20;

strcpy(names[0],"lon");
strcpy(names[1],"lat");
strcpy(names[2],"press");
strcpy(names[3],"o2");
nn=4;

strcpy(objname,argv[1]);
maxlev=jdbopen_(&unit,objname,names,&namesize,values,&valuesize,&nn);
if(maxlev<0){printf("Bad object %s\n",argv[1]);exit(1);};

*
while(jdbcomments_(&unit,objname))printf("#%s\n",objname);
/

for(i=0;i<nn;i++)printf("%-20.20s",names[i]);
printf("\n");
*
for(i=0;i<nn;i++)
  while(jdbattributes_(&unit,&i,objname))printf("%s . %s \n",names[i],objname);
for(i=0;i<nn;i++)printf("%-20d",levels[i]);
printf("\n");
for(i=0;i<nn;i++)printf("%-20d",sizes[i]);
printf("\n");
/

while((clev=jdbreada_(&unit,values,&valuesize)) >= 0){
  if(clev<maxlev) while(jdbcomments_(&unit,objname))printf("#%s\n",objname);
  for(i=0;i<nn;i++)printf("%-20.20s",values[i]);
  printf("\n");
  };
jdbclose_(&unit);
}
*/
