/* jdb.c - forms primary routines for the JGOFS data system */ /* used from jgofs.a */ char jdb_id[]="jdb version 1.1d 29 Jul 2003"; /* 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' */ #ifndef SOL #define SOL 0 #endif #ifndef IBM #define IBM 0 #endif #ifndef HP #define HP 0 #endif #ifndef VMS #define VMS 0 #endif #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; #include #include #include #include #if SOL || HP #include #include #endif #if VMS #include #endif /* added for JGOFS release v1.5 compatibility (not clear which parts of */ /* these 2 files are actually required - WJS) */ #include OPTIONS #include INNEROPTIONS int dctsearch(); int HTDoConnect(); int HTDoRead(); 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("
DATA!!!%s!!!ENDDATA\n
",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+1; 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"); exit(1); } switch(childpid = fork()) { case -1: perror("bad fork"); exit(1); 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; isocket=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 jdb: data line too big. Start of line: %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],met[MAX_BUF],par[MAX_BUF],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 Error - not found: %s %s\n",met,comm); exit(1); } 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 %s opening remote object: %s\n",cp,met); return i; } } else if (localopen(met,par,o) != 0) { printf("&x Error opening local object: %s\n",met); return -999; } o->htbufflen=0; o->htbuffptr=0; *(o->comments)='\0'; o->commentptr = o->comments; for(i=0;irdpntr[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 jdb: data line too big. Start of line: %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 jdb: data line too big. Start of line: %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 Malformed attribute list (missing ]): %s\n",ap); return -997; } }; o->ntotal++; if(numd < 0){ (*num)++; if(*num > -numd){ printf("&x Too many variables (max = %d)\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 jdb: data line too big. Start of line: %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];ilvlpntr[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 jdb: data line too big. Start of line: %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(ilvlcomments)='\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];ilvlpntr[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;jntotal;j++)if(*varnum==o->rdpntr[j])break; if(j>=o->ntotal)return -1; for(i=1;i<=o->maxlevel;i++)if(jlvlpntr[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= 0){ if(clev