/*!\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) // _printf_(true,"tag buffer (length=%d):\n",ibuf); // else if (ifield) // _printf_(true,"field buffer (length=%d):\n",ibuf); // _printf_(true,"%s\n",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; } // _printf_(true,"comment buffer (length=%d):\n",ibuf); // _printf_(true,"%s\n",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)) _error_("KMLFileTagName -- Missing tag delimiters in %s.\n",ktag); /* 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,"< >"); // _printf_(true,"KMLFileTagName -- initial token=\"%s\".\n",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))) { _printf_(true,"KMLFileTagName -- string field too short for %s.\n",ktag); _printf_(true,"KMLFileTagName -- \"%s\" truncated to %d characters.\n",ktokn,maxlen); 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," "); // _printf_(true,"KMLFileTagAttrib -- initial token=\"%s\".\n",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); // _printf_(true,"KMLFileTagAttrib -- attribute %s=\"%s\".\n",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; // _printf_(true,"KMLFileTagAttrib -- isolo=%d.\n",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] == '<')) _error_("KMLFileTokenParse -- Missing integer field for %s.\n",ktag); 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))) _error_("KMLFileTokenParse -- Missing closing tag for %s.\n",ktag); else xfree((void**)&kstr); // _printf_(true,"KMLFileTokenParse -- %s=%d.\n",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] == '<')) _error_("KMLFileTokenParse -- Missing bool field for %s.\n",ktag); 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))) _error_("KMLFileTokenParse -- Missing closing tag for %s.\n",ktag); else xfree((void**)&kstr); // _printf_(true,"KMLFileTokenParse -- %s=%s.\n",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] == '<')) _error_("KMLFileTokenParse -- Missing string field for %s.\n",ktag); 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))) { _printf_(true,"KMLFileTokenParse -- string field too short for %s.\n",ktag); _printf_(true,"KMLFileTokenParse -- \"%s\" truncated to %d characters.\n",kstr,maxlen); 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))) _error_("KMLFileTokenParse -- Missing closing tag for %s.\n",ktag); else xfree((void**)&kstr); // _printf_(true,"KMLFileTokenParse -- %s=\"%s\".\n",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] == '<')) _error_("KMLFileTokenParse -- Missing integer field for %s.\n",ktag); 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))) _error_("KMLFileTokenParse -- Missing closing tag for %s.\n",ktag); else xfree((void**)&kstr); // _printf_(true,"KMLFileTokenParse -- %s=%g.\n",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] == '<')) _error_("KMLFileTokenParse -- Missing integer field for %s.\n",ktag); 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))) _error_("KMLFileTokenParse -- Missing closing tag for %s.\n",ktag); else xfree((void**)&kstr); // _printf_(true,"KMLFileTokenParse -- %s=%g.\n",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] == '<')) _error_("KMLFileTokenParse -- Missing double [m] field for %s.\n",ktag); 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)) _error_("KMLFileTokenParse -- Double [m] field too short for %s.\n",ktag); 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))) _error_("KMLFileTokenParse -- Missing closing tag for %s.\n",ktag); else xfree((void**)&kstr); // _printf_(true,"KMLFileTokenParse -- %s=...\n",ktag); // for (j=0; j<=i; j++) // _printf_(true," [%d]: %lg\n",j,(*pdval)[j]); 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] == '<')) _error_("KMLFileTokenParse -- Missing double [m x 3] field for %s.\n",ktag); 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)) _error_("KMLFileTokenParse -- Double [m x 3] field too short for %s.\n",ktag); } 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) _printf_(true,"KMLFileTokenParse -- Double [m x 3] field for %s does not have multiple of 3 values.\n",ktag); /* 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))) _error_("KMLFileTokenParse -- Missing closing tag for %s.\n",ktag); else xfree((void**)&kstr); // _printf_(true,"KMLFileTokenParse -- %s=...\n",ktag); // for (j=0; j<=i; j++) // _printf_(true," [%d][0-2]: %lg,%lg,%lg\n",j,(*pdval3)[j][0],(*pdval3)[j][1],(*pdval3)[j][2]); 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 */ _printf_(true,"KMLFileTagSkip -- input tag %s.\n",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)))) { _printf_(true,"KMLFileTagSkip -- closing tag %s.\n",kstr); xfree((void**)&kstr); return(0); } /* if next token is an opening tag, call recursively */ else if ((kstr[0] == '<') && (kstr[1] != '/')) { _printf_(true,"KMLFileTagSkip -- opening tag %s.\n",kstr); KMLFileTagSkip(kstr, fid); } /* if next token is a closing tag, error out */ else if ((kstr[0] == '<') && (kstr[1] == '/')) { _error_("KMLFileTagSkip -- Unexpected closing tag %s.\n",kstr); } xfree((void**)&kstr); } _error_("KMLFileTokenParse -- Corresponding closing tag for %s not found.\n",ktag); return(0); } /*}}}*/