/*!\file KMLFileUtils.cpp * \brief: utilities for kml file reading. */ /*Headers:*/ /*{{{*/ #ifdef HAVE_CONFIG_H #include #else #error "Cannot compile with HAVE_CONFIG_H symbol! run configure first!" #endif #include #include #include "../objects.h" #include "../../shared/shared.h" #include "../../io/io.h" #include "../../Container/Container.h" #include "../../include/include.h" /*}}}*/ /*FUNCTION KMLFileToken(FILE* fid,int* pncom=NULL,char*** ppcom=NULL) {{{*/ char* KMLFileToken(FILE* fid, int* pncom=NULL,char*** ppcom=NULL){ /* get the next token (tag or field) in the file */ bool inew=1,itag=0,ifield=0; int c; int ibuf=0,buflen=1024,bufblk=1024; char *buffer=NULL,*bufferc=NULL; buffer=(char *) xmalloc(buflen*sizeof(char)); buffer[0]='\0'; /* read kml file character-by-character */ // note that fgets includes newline // fgets(buffer,buflen,fid); while ((c=getc(fid)) != EOF) { /* ignore leading blanks */ if (inew && isspace(c)) continue; /* distinguish between tag or field */ if (!itag && !ifield) { /* distinguish between tag or comment */ if (c == '<') { ungetc(c,fid); if (!(bufferc=KMLFileTokenComment(fid))) { c=getc(fid); itag=1; } else { if (pncom && ppcom) { (*pncom)++; *ppcom=(char **) xrealloc(*ppcom,*pncom*sizeof(char*)); (*ppcom)[*pncom-1]=bufferc; } else xfree((void**)&bufferc); inew=1; continue; } } else ifield=1; inew=0; KMLFileTokenBuffer(&buffer,&ibuf,&buflen, c, bufblk); } /* accumulate tag, not including newlines */ else if (itag) { if (c != '\n') { inew=0; KMLFileTokenBuffer(&buffer,&ibuf,&buflen, c, bufblk); if (c == '>') break; } else inew=1; } /* accumulate field, including newlines */ else if (ifield) { /* distinguish between another tag or comment */ if (c == '<') { ungetc(c,fid); if (!(bufferc=KMLFileTokenComment(fid))) break; else if (pncom && ppcom) { (*pncom)++; *ppcom=(char **) xrealloc(*ppcom,*pncom*sizeof(char*)); (*ppcom)[*pncom-1]=bufferc; } else xfree((void**)&bufferc); } else { inew=0; KMLFileTokenBuffer(&buffer,&ibuf,&buflen, c, bufblk); if (c == '\n') inew=1; } } } /* remove trailing blanks or newline */ while (ibuf > 0) if (isspace(buffer[ibuf-1])) ibuf--; else { buffer[ibuf]='\0'; break; } // if (itag) // if(true) _pprintLine_("tag buffer (length=" << ibuf << "):"); // else if (ifield) // if(true) _pprintLine_("field buffer (length=" << ibuf << "):"); // if(true) _pprintLine_("" << buffer); if (!ibuf) xfree((void**)&buffer); return(buffer); } /*}}}*/ /*FUNCTION KMLFileTokenComment(FILE* fid) {{{*/ char* KMLFileTokenComment(FILE* fid){ /* check for comment in the file and read it */ bool inew=1; int i; int c; int ibuf=0,buflen=1024,bufblk=1024; char* buffer=NULL; buffer=(char *) xmalloc(buflen*sizeof(char)); buffer[0]='\0'; /* read kml file character-by-character */ while ((c=getc(fid)) != EOF) { /* ignore leading blanks */ if (inew && isspace(c)) continue; inew=0; KMLFileTokenBuffer(&buffer,&ibuf,&buflen, c, bufblk); /* check for comment */ if (ibuf <= 4) { if ((ibuf == 1 && buffer[0] != '<') || (ibuf == 2 && buffer[1] != '!') || (ibuf == 3 && buffer[2] != '-') || (ibuf == 4 && buffer[3] != '-')) { for (i=ibuf-1; i>=0; i--) ungetc(buffer[i],fid); xfree((void**)&buffer); return(buffer); } } /* accumulate comment, including newlines */ else if (buffer[ibuf-3]=='-' && buffer[ibuf-2]=='-' && buffer[ibuf-1]=='>') break; } /* remove trailing blanks or newline */ while (ibuf > 0) if (isspace(buffer[ibuf-1])) ibuf--; else { buffer[ibuf]='\0'; break; } // if(true) _pprintLine_("comment buffer (length=" << ibuf << "):"); // if(true) _pprintLine_("" << buffer); if (!ibuf) xfree((void**)&buffer); return(buffer); } /*}}}*/ /*FUNCTION KMLFileTokenBuffer {{{*/ void KMLFileTokenBuffer(char** pbuffer,int* pibuf,int* pbuflen, int c, int bufblk){ /* add the specified character to the token buffer */ char* buffer=NULL; /* check buffer length and realloc if necessary */ if (*pibuf+2 > *pbuflen) { *pbuflen+=bufblk; *pbuffer=(char *) xrealloc(*pbuffer,*pbuflen*sizeof(char)); } /* add character and terminator */ (*pbuffer)[(*pibuf)++]=c; (*pbuffer)[ *pibuf ]='\0'; return; } /*}}}*/ /*FUNCTION KMLFileTagName {{{*/ char* KMLFileTagName(char* pname, char* ktag){ return(KMLFileTagName(pname,NULL,0, ktag)); } /*}}}*/ /*FUNCTION KMLFileTagName {{{*/ char* KMLFileTagName(char* pname,int *m,int maxlen, char* ktag){ /* for the given tag buffer, read and store the name */ char* ktagi; char* ktokn; if (strncmp(&ktag[0],"<" ,1) || strncmp(&ktag[strlen(ktag)-1],">",1)) _error2_("KMLFileTagName -- Missing tag delimiters in " << ktag << ".\n"); /* strtok modifies ktag, so work on copy */ ktagi=(char *) xmalloc((strlen(ktag)+1)*sizeof(char)); memcpy(ktagi,ktag,(strlen(ktag)+1)*sizeof(char)); /* skip opening delimeter and find subsequent blank or closing delimiter */ ktokn=strtok(ktagi,"< >"); // if(true) _pprintLine_("KMLFileTagName -- initial token=\"" << ktokn << "\"."); if (!pname) { if (maxlen) pname=(char *) xmalloc((maxlen +1)*sizeof(char)); else pname=(char *) xmalloc((strlen(ktokn)+1)*sizeof(char)); } if (maxlen && (maxlen < strlen(ktokn))) { if(true) _pprintLine_("KMLFileTagName -- string field too short for " << ktag << "."); if(true) _pprintLine_("KMLFileTagName -- \"" << ktokn << "\" truncated to " << maxlen << " characters."); strncpy(pname,ktokn,maxlen); } else memcpy(pname,ktokn,(strlen(ktokn)+1)*sizeof(char)); xfree((void**)&ktagi); if (m) *m=strlen(pname); return(pname); } /*}}}*/ /*FUNCTION KMLFileTagAttrib {{{*/ int KMLFileTagAttrib(KML_Object* kobj, char* ktag){ /* for the given tag buffer, read and store the attributes */ char* ktagi; char* ktokn; char* ktokv; char quote[]={'\"','\0'}; int isolo=0; /* strtok modifies ktag, so work on copy */ ktagi=(char *) xmalloc((strlen(ktag)+1)*sizeof(char)); memcpy(ktagi,ktag,(strlen(ktag)+1)*sizeof(char)); /* loop through tag to find all attributes */ /* return first non blank and move past subsequent blank */ ktokn=strtok(ktagi," "); // if(true) _pprintLine_("KMLFileTagAttrib -- initial token=\"" << ktokn << "\"."); /* return next non " =?/>" and move past subsequent " =?/>" */ while (ktokn=strtok(NULL," =?/>")) { /* return next non quote and move past subsequent quote */ ktokv=strtok(NULL,quote); // if(true) _pprintLine_("KMLFileTagAttrib -- attribute " << ktokn << "=\"" << ktokv << "\"."); /* add the attribute to the dataset */ if (kobj) kobj->AddAttrib(ktokn,ktokv); } xfree((void**)&ktagi); /* check for xml declaration, dtd declaration, or solo tag */ if ((!strncmp(&ktag[0],"",2)) || (!strncmp(&ktag[0],"",1)) || (!strncmp(&ktag[0],"<" ,1) && !strncmp(&ktag[strlen(ktag)-2],"/>",2))) isolo=1; // if(true) _pprintLine_("KMLFileTagAttrib -- isolo=" << isolo << "."); return(isolo); } /*}}}*/ /*FUNCTION KMLFileTokenParse {{{*/ int KMLFileTokenParse(int* pival, char* ktag, FILE* fid){ char* kstr; /* get next token and convert to appropriate format */ if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] == '<')) _error2_("KMLFileTokenParse -- Missing integer field for " << ktag << ".\n"); sscanf(kstr,"%d",pival); xfree((void**)&kstr); /* get additional token and compare to closing tag */ if (ktag) if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] != '<') || (kstr[1] != '/') || (strncmp(&(kstr[2]),&(ktag[1]),strlen(ktag)-1))) {_error2_("KMLFileTokenParse -- Missing closing tag for " << ktag << ".\n");} else xfree((void**)&kstr); // if(true) _pprintLine_("KMLFileTokenParse -- " << ktag << "=" << *pival << "."); return(0); } /*}}}*/ /*FUNCTION KMLFileTokenParse {{{*/ int KMLFileTokenParse(bool* pbval, char* ktag, FILE* fid){ int ival; char* kstr; /* get next token and convert to appropriate format */ if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] == '<')) {_error2_("KMLFileTokenParse -- Missing bool field for " << ktag << ".\n");} sscanf(kstr,"%d",&ival); *pbval=(bool)ival; xfree((void**)&kstr); /* get additional token and compare to closing tag */ if (ktag) if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] != '<') || (kstr[1] != '/') || (strncmp(&(kstr[2]),&(ktag[1]),strlen(ktag)-1))) {_error2_("KMLFileTokenParse -- Missing closing tag for " << ktag << ".\n");} else xfree((void**)&kstr); // if(true) _pprintLine_("KMLFileTokenParse -- " << ktag << "=" << (*pbval ? "true" : "false") << "."); return(0); } /*}}}*/ /*FUNCTION KMLFileTokenParse {{{*/ char* KMLFileTokenParse(char* pstr, char* ktag, FILE* fid){ return(KMLFileTokenParse(pstr,NULL,0, ktag, fid)); } /*}}}*/ /*FUNCTION KMLFileTokenParse {{{*/ char* KMLFileTokenParse(char* pstr,int *m,int maxlen, char* ktag, FILE* fid){ char* kstr; /* get next token and allocate if necessary */ if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] == '<')) _error2_("KMLFileTokenParse -- Missing string field for " << ktag << ".\n"); if (!pstr) { if (maxlen) pstr=(char *) xmalloc((maxlen +1)*sizeof(char)); else pstr=(char *) xmalloc((strlen(kstr)+1)*sizeof(char)); } if (maxlen && (maxlen < strlen(kstr))) { if(true) _pprintLine_("KMLFileTokenParse -- string field too short for " << ktag << "."); if(true) _pprintLine_("KMLFileTokenParse -- \"" << kstr << "\" truncated to " << maxlen << " characters."); strncpy(pstr,kstr,maxlen); } else memcpy(pstr,kstr,(strlen(kstr)+1)*sizeof(char)); xfree((void**)&kstr); if (m) *m=strlen(pstr); /* get additional token and compare to closing tag */ if (ktag) if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] != '<') || (kstr[1] != '/') || (strncmp(&(kstr[2]),&(ktag[1]),strlen(ktag)-1))) {_error2_("KMLFileTokenParse -- Missing closing tag for " << ktag << ".\n");} else xfree((void**)&kstr); // if(true) _pprintLine_("KMLFileTokenParse -- " << ktag << "=\"" << pstr << "\"."); return(pstr); } /*}}}*/ /*FUNCTION KMLFileTokenParse {{{*/ int KMLFileTokenParse(float* pfval, char* ktag, FILE* fid){ char* kstr; /* get next token and convert to appropriate format */ if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] == '<')) {_error2_("KMLFileTokenParse -- Missing integer field for " << ktag << ".\n");} sscanf(kstr,"%g",pfval); xfree((void**)&kstr); /* get additional token and compare to closing tag */ if (ktag) if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] != '<') || (kstr[1] != '/') || (strncmp(&(kstr[2]),&(ktag[1]),strlen(ktag)-1))) {_error2_("KMLFileTokenParse -- Missing closing tag for " << ktag << ".\n");} else xfree((void**)&kstr); // if(true) _pprintLine_("KMLFileTokenParse -- " << ktag << "=" << *pfval << "."); return(0); } /*}}}*/ /*FUNCTION KMLFileTokenParse {{{*/ int KMLFileTokenParse(double* pdval, char* ktag, FILE* fid){ char* kstr; /* get next token and convert to appropriate format */ if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] == '<')) _error2_("KMLFileTokenParse -- Missing integer field for " << ktag << ".\n"); sscanf(kstr,"%lg",pdval); xfree((void**)&kstr); /* get additional token and compare to closing tag */ if (ktag) if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] != '<') || (kstr[1] != '/') || (strncmp(&(kstr[2]),&(ktag[1]),strlen(ktag)-1))) {_error2_("KMLFileTokenParse -- Missing closing tag for " << ktag << ".\n");} else xfree((void**)&kstr); // if(true) _pprintLine_("KMLFileTokenParse -- " << ktag << "=" << *pdval << "."); return(0); } /*}}}*/ /*FUNCTION KMLFileTokenParse {{{*/ int KMLFileTokenParse(double **pdval,int* m,int maxlen, char* ktag, FILE* fid){ int i=-1,j; char* kstr; char* ktok; char delim[]={' ',',','\f','\n','\r','\t','\v','\0'}; /* get next token and allocate if necessary */ if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] == '<')) _error2_("KMLFileTokenParse -- Missing double [m] field for " << ktag << ".\n"); if (!*pdval) if (maxlen) *pdval=(double *) xmalloc(maxlen *sizeof(double)); else *pdval=(double *) xmalloc(((strlen(kstr)+1)/2)*sizeof(double)); /* loop through string to get all values */ ktok=strtok(kstr,delim); while (ktok) { i++; if (maxlen && (maxlen < i+1)) _error2_("KMLFileTokenParse -- Double [m] field too short for " << ktag << ".\n"); sscanf(ktok,"%lg",&((*pdval)[i])); ktok=strtok(NULL,delim); } xfree((void**)&kstr); if (!maxlen) *pdval=(double *) xrealloc(*pdval,(i+1)*sizeof(double)); if (m) *m=i+1; /* get additional token and compare to closing tag */ if (ktag) if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] != '<') || (kstr[1] != '/') || (strncmp(&(kstr[2]),&(ktag[1]),strlen(ktag)-1))) {_error2_("KMLFileTokenParse -- Missing closing tag for " << ktag << ".\n");} else xfree((void**)&kstr); // if(true) _pprintLine_("KMLFileTokenParse -- " << ktag << "=..."); // for (j=0; j<=i; j++) // if(true) _pprintLine_(" [" << j << "]: " << (*pdval)[j] << "g"); return(0); } /*}}}*/ /*FUNCTION KMLFileTokenParse {{{*/ int KMLFileTokenParse(double (**pdval3)[3],int* m,int maxlen, char* ktag, FILE* fid){ int i=0,j=-1; char* kstr; char* ktok; char delim[]={' ',',','\f','\n','\r','\t','\v','\0'}; /* get next token and allocate if necessary */ if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] == '<')) _error2_("KMLFileTokenParse -- Missing double [m x 3] field for " << ktag << ".\n"); if (!*pdval3) if (maxlen) *pdval3=(double (*)[3]) xmalloc((maxlen*3) *sizeof(double)); else *pdval3=(double (*)[3]) xmalloc(((strlen(kstr)+1)/2)*sizeof(double)); /* loop through string to get all values */ ktok=strtok(kstr,delim); while (ktok) { j++; if (j == 3) { i++; j=0; if (maxlen && (maxlen < i+1)) _error2_("KMLFileTokenParse -- Double [m x 3] field too short for " << ktag << ".\n"); } sscanf(ktok,"%lg",&((*pdval3)[i][j])); ktok=strtok(NULL,delim); } xfree((void**)&kstr); if (!maxlen) *pdval3=(double (*)[3]) xrealloc(*pdval3,((i+1)*3)*sizeof(double)); if (m) *m=i+1; if (j != 2) if(true) _pprintLine_("KMLFileTokenParse -- Double [m x 3] field for " << ktag << " does not have multiple of 3 values."); /* get additional token and compare to closing tag */ if (ktag) if (!(kstr=KMLFileToken(fid, NULL,NULL)) || (kstr[0] != '<') || (kstr[1] != '/') || (strncmp(&(kstr[2]),&(ktag[1]),strlen(ktag)-1))) {_error2_("KMLFileTokenParse -- Missing closing tag for " << ktag << ".\n");} else xfree((void**)&kstr); // if(true) _pprintLine_("KMLFileTokenParse -- " << ktag << "=..."); // for (j=0; j<=i; j++) // if(true) _pprintLine_(" [" << j << "][0-2]: " << (*pdval3)[j][0] << "g," << (*pdval3)[j][1] << "g," << (*pdval3)[j][2] << "g"); return(0); } /*}}}*/ /*FUNCTION KMLFileTagSkip {{{*/ int KMLFileTagSkip(char* ktag, FILE* fid){ char* kstr; /* note that tags of the same type can be nested inside each other, so for each opening tag, must find corresponding closing tag */ if(true) _pprintLine_("KMLFileTagSkip -- input tag " << ktag << "."); /* if next token is a closing tag, compare to input */ while (kstr=KMLFileToken(fid, NULL,NULL)) { if ((kstr[0] == '<') && (kstr[1] == '/') && (!strncmp(&(kstr[2]),&(ktag[1]),(strcspn(ktag," >")-1)/sizeof(char)))) { if(true) _pprintLine_("KMLFileTagSkip -- closing tag " << kstr << "."); xfree((void**)&kstr); return(0); } /* if next token is an opening tag, call recursively */ else if ((kstr[0] == '<') && (kstr[1] != '/')) { if(true) _pprintLine_("KMLFileTagSkip -- opening tag " << kstr << "."); KMLFileTagSkip(kstr, fid); } /* if next token is a closing tag, error out */ else if ((kstr[0] == '<') && (kstr[1] == '/')) { _error2_("KMLFileTagSkip -- Unexpected closing tag " << kstr << ".\n"); } xfree((void**)&kstr); } _error2_("KMLFileTokenParse -- Corresponding closing tag for " << ktag << " not found.\n"); return(0); } /*}}}*/