// tttfield.c // // by John D. de Boer #include "twist.h" #include "ttt.h" extern char *MonthName[]; extern int UNIQUEMOCHAR[1]; // Filenames string GetTTTFileName(const char *S) { char *Q1,*Q2,*N; long L; if (!S || !*S) return NULL; Q1=firstocc('"',S); if (!Q1) return NULL; Q2=firstocc('"',Q1 + 1); if (!Q2) return NULL; L= Q2 - Q1 - 1; if (L<1) return NULL; N=leftstr(Q1 + 1,L); //replace(&N,"../","::"); repover(N,"/",":"); //if (firstocc(':',N) && !begins(N,":")) N=cat(2,":",N); return N; } static string remquotes(const char *S) { char *Q1,*Q2; long L; if (!S || !*S) return NULL; Q1=firstocc('"',S); if (!Q1) return copyof(S); Q2=firstocc('"',Q1 + 1); if (!Q2) return copyof(S); L= Q2 - Q1 - 1; if (L<1) return NULL; return leftstr(Q1 + 1,L); } // Entities void repentity(char *S) { char *F; if (!S) return; F=firstocc('&',S); if (!F) return; repover(F,"<","<"); repover(F,">",">"); repover(F,"£","#"); repover(F,"¶","\r\t"); } // Parsing fields static stringlist UFList; // field names that were not recognised static void UnknownField(string S) { if (stringisinlist(&UFList,S)) { old(S); return; } WarnOld(cat(0,"Unrecognised field: ",S)); addstringtolist(&UFList,S); } int FirstField(string *F, const char **S) { char *T,*U,*B,*E; int N; if (!F) return fNone; *F=NULL; if (!S || !*S || !**S) return fNone; T=firstocc('<',*S); if (!T) return fNone; B=firstocc('>',T); if (!B) return fNone; E=firstocc('<',B); *S=E; if (!E) { E=B; while (*E) E++; } while (!letter(*T) && T=B) return fNone; U=T; while (letter(*U)) U++; B++; while (*B==' ') B++; if (*B && BB && E[-1]==' ') E--; if (E>B) *F=leftstr(B,E - B); } N= U - T; if (abbrev(T,N,"name", 1)) return fName; // fields for #city if (abbrev(T,N,"aka", 3)) return fAka; if (abbrev(T,N,"latlong", 1)) return fGeoRef; if (abbrev(T,N,"radius", 3)) return fRadius; if (abbrev(T,N,"nation", 3)) return fNation; if (abbrev(T,N,"flag", 3)) return fFlag; if (abbrev(T,N,"region", 3)) return fRegion; if (abbrev(T,N,"replace", 3)) return fReplace; if (abbrev(T,N,"colour", 3)) return fColour; if (abbrev(T,N,"water", 3)) return fWater; if (abbrev(T,N,"female", 3)) return fFemale; // more fields for #person if (abbrev(T,N,"born", 1)) return fBorn; if (abbrev(T,N,"died", 1)) return fDied; if (abbrev(T,N,"translated", 2)) return fTrans; if (abbrev(T,N,"generation", 3)) return fGeneration; if (abbrev(T,N,"surname", 3)) return fSurname; if (abbrev(T,N,"father", 2)) return fFather; if (abbrev(T,N,"mother", 2)) return fMother; if (abbrev(T,N,"paternal", 3)) return fPatAnc; if (abbrev(T,N,"maternal", 3)) return fMatAnc; if (abbrev(T,N,"marriage", 2)) return fMarriage; if (abbrev(T,N,"city", 1)) return fCity; if (abbrev(T,N,"depart", 3)) return fDepart; if (abbrev(T,N,"arrive", 3)) return fArrive; if (abbrev(T,N,"move", 3)) return fMove; if (abbrev(T,N,"occupation", 1)) return fOccup; if (abbrev(T,N,"acceded", 2)) return fAcceded; if (abbrev(T,N,"abdicated", 2)) return fAbdicate; if (abbrev(T,N,"appointed", 3)) return fAppointed; if (abbrev(T,N,"deposed", 4)) return fDeposed; if (abbrev(T,N,"elected", 2)) return fElected; if (abbrev(T,N,"retired", 2)) return fRetired; if (abbrev(T,N,"power", 3)) return fPower; if (abbrev(T,N,"information",1)) return fInfo; if (abbrev(T,N,"reference", 3)) return fRef; if (abbrev(T,N,"picture", 4)) return fPict; if (abbrev(T,N,"caption", 3)) return fCaption; if (abbrev(T,N,"date", 1)) return fDate; // more fields for #event if (abbrev(T,N,"end", 1)) return fEnd; if (abbrev(T,N,"type", 1)) return fType; if (abbrev(T,N,"person", 1)) return fPerson; if (abbrev(T,N,"polygon", 4)) return fPolygon; // more fields for #sphere if (abbrev(T,N,"icon", 4)) return fIcon; // more fields for #item if (abbrev(T,N,"off", 3)) return fOff; UnknownField(leftstr(T,N)); return fNone; } // Parsing numbers static long ParseNumeral(const char *B, const char *E) { long R; if (!B || !E) return 0; R=0; while (B4 || *E!='.') return; YEAR=ParseNumeral(S,E); S= E + 1; if (!*S || !numeral(*S)) return; E= (char *)S + 1; while (numeral(*E)) E++; L= E - S; if (L!=2 || *E!='.') return; MONTH= ParseNumeral(S,E) - 1; if (!range(MONTH,12)) return; S= E + 1; if (!*S || !numeral(*S)) return; E= (char *)S + 1; while (numeral(*E)) E++; L= E - S; if (L!=2) return; DATE= ParseNumeral(S,E) - 1; if (!range(MONTH,31)) return; D->jd=datetojd(YEAR,MONTH,DATE); D->unc=0; D->kind=timeJD; S=E; while (*S==' ') S++; if (!*S) return; // normal return if (*S=='('/*)*/) D->unc=ParseUncertainty(S + 1); } // normal return static bool ParseSeason(const char *S, int *SEASON) { if (!S || !SEASON) return 0; if (*S==',') S++; while (*S==' ') S++; if (streqci(S,"winter")) { *SEASON=0; return 1; } if (streqci(S,"spring")) { *SEASON=1; return 1; } if (streqci(S,"summer")) { *SEASON=2; return 1; } if (streqci(S,"autumn") || streqci(S,"fall")) { *SEASON=3; return 1; } return 0; } static int twparsemonth(const char *S, long L) { int i; for (i=0;i<12;i++) if (abbrev(S,L,MonthName[i],UNIQUEMOCHAR[i])) return i; WarnOld(cat3(2,"Month \"",leftstr(S,L),"\" not understood")); return -1; } static void ParseDateS(const char *S, bool AD, Day *D) { int YEAR,MONTH,DATE,SEASON; char *E; long L; bool Oblique,Two; int DELTA; if (!D) return; ParseISODate(S,D); if (D->kind==timeJD) return; // normal return InitDay(D); if (!S || !*S) return; while (*S==' ') S++; if (!*S) return; if (begins(S,"A.D.")) { AD=1; S+=4; } if (begins(S,"B.C.")) { AD=0; S+=4; } while (*S==' ') S++; if (!*S) return; E=(char *)S; while (numeral(*E)) E++; L= E - S; if (L<1) { return; } YEAR=ParseNumeral(S,E); Oblique=(E[0]=='/'); if (Oblique) { if (!numeral(E[1])) return; Two=numeral(E[2]); if (Two && numeral(E[3])) return; if (Two && E - S > 1) { DELTA= (10 * (E[1] - E[-2])) + E[2] - E[-1]; E+=3; } else if (Two) { if (E[1]!='1') return; DELTA= 10 + E[2] - E[-1]; E+=3; } // "9/10" else { DELTA= E[1] - E[-1]; E+=2; } } S=E; while (*S==' ') S++; if (begins(S,"A.D.")) { AD=1; S+=4; } if (begins(S,"B.C.")) { AD=0; S+=4; } if (!AD) YEAR=-YEAR; if (YEAR==0) YEAR=-1; D->jd=datetojd(YEAR,6,0); D->unc=180/*183*/; D->kind=timeJD; while (*S==' ') S++; if (!*S && !Oblique) return; // normal return if (Oblique) { if (*S) { D->kind=timeInval; return; } if (YEAR<0 && DELTA!=-1) { D->kind=timeInval; return; } if (YEAR>0 && DELTA!=1) { D->kind=timeInval; return; } D->jd=datetojd(YEAR,11,30); D->unc=365; return; } // normal return if (*S=='('/*)*/) { D->unc= (365 * ParseUncertainty(S + 1)) + 182; return; } // normal return if (ParseSeason(S,&SEASON)) { D->jd=datetojd(YEAR,(SEASON * 3) + 1,5); D->unc=45; return; } // normal return E=(char *)S; while (letter(*E)) E++; L= E - S; if (L<1) { return; } MONTH=twparsemonth(S,L); if (MONTH<0) return; D->jd=datetojd(YEAR,MONTH,14/*15*/); D->unc=14/*15*/; S=E; while (*S==' ') S++; if (!*S) return; // normal return if (*S=='('/*)*/) { D->unc= (30 * ParseUncertainty(S + 1)) + 15; return; } // normal return E=(char *)S; while (numeral(*E)) E++; L= E - S; if (L<1) { return; } DATE= ParseNumeral(S,E) - 1; if (DATE>30) { D->kind=timeInval; return; } D->jd=datetojd(YEAR,MONTH,DATE); D->unc=0; S=E; while (*S==' ') S++; if (!*S) return; // normal return if (*S=='('/*)*/) D->unc=ParseUncertainty(S + 1); } // normal return void ParseDate(const char *S, bool AD, Day *D) { ParseDateS(S,AD,D); if (D->kind==timeInval && S && *S) WarnOld(cat(0,"Date format error: ",(char *)S)); } void ParseRelDate(const char *S, const Day *A, Day *D) { short YEAR; int LATER; char *E; if (!D) return; InitDay(D); if (!A || A->kind!=timeJD) return; if (!S || !*S) return; while (*S==' ') S++; if (!*S) return; if (begins(S,"age ")) S+=4; while (*S==' ') S++; if (!*S) return; E=(char *)S; while (numeral(*E)) E++; if (E==S) return; LATER=ParseNumeral(S,E); jdtodate(A->jd,&YEAR,NULL,NULL); LATER+=YEAR; if (YEAR<1 && LATER>=0) LATER++; *D=*A; D->jd=datetojd(LATER,6,0); S=E; while (*S==' ') S++; if (!*S) return; // normal return if (*S=='('/*)*/) D->unc+= 365 * ParseUncertainty(S + 1); } // normal return // Parsing georeferences static bool ParseGeoCoord(long *C, const char *S, const char *T) { long DEG,MIN; char *E; if (!C || !S || !T) return 0; while (!numeral(*S) && S=T) return 0; E=(char *)S; while (numeral(*E)) E++; switch (E - S) { case 5: DEG=ParseNumeral(S,S + 3); MIN=ParseNumeral(S + 3,E); break; case 4: DEG=ParseNumeral(S,S + 2); MIN=ParseNumeral(S + 2,E); break; case 3: DEG=ParseNumeral(S,S + 3); MIN=0; break; case 2: DEG=ParseNumeral(S,S + 2); MIN=0; break; default: return 0; } *C= (60 * DEG) + MIN; return 1; } static void ParseGeoRefS(GeoRef *G, const char *S) { char *N,*E; if (!G) return; G->good=0; if (!S || !*S) return; N=firstoccoflist("NnSs",S); if (!N) return; E=firstoccoflist("EeWw",S); if (!E) return; if (!ParseGeoCoord(&(G->lat),S,N)) return; if (!ParseGeoCoord(&(G->lng),N + 1,E)) return; if (firstocc(*N,"Ss")) G->lat=-G->lat; if (firstocc(*E,"Ww")) G->lng=-G->lng; G->good=1; } void ParseGeoRef(GeoRef *G, const char *S) { ParseGeoRefS(G,S); if (!G->good) WarnOld(cat(0,"Waypoint georef error: ",(char *)S)); } // Parsing colour fields static bool ParseRGBColour(colour *C, const char *S) { char *E; float R,G,B; if (!C || !S || !*S) return 0; setcolour(C,white); while (*S==' ') S++; if (!*S) return 0; E=(char *)S; while (*E && *E!=' ') E++; if (!*E) return 0; R=ParseNumeral(S,E); S=E; while (*S==' ') S++; if (!*S) return 0; E=(char *)S; while (*E && *E!=' ') E++; if (!*E) return 0; G=ParseNumeral(S,E); S=E; while (*S==' ') S++; if (!*S) return 0; E=(char *)S; while (*E && *E!=' ') E++; B=ParseNumeral(S,E); setrgb(C,R/100.0,G/100.0,B/100.0); return 1; } void ParseColour(colour *C, const char *S) { if (ParseRGBColour(C,S)) return; if (parsecolourname(S,C)) return; WarnOld(cat3(0,"Not able to parse a colour: \"",(char *)S,"\"")); } // Parsing polygons vectorpoly **ParsePolygon(const char *S, const FSRef *FSR) { FSRef New; file F; bool R; polygon **P; vectorpoly **VP; char *N,*T,*U; if (!S || !FSR) return NULL; N=remquotes(S); if (!N) return NULL; if (!N) { Warning("filename missing in field"); return NULL; } R=relfsref(FSR,N,&New); if (!R) { WarnOld(cat3(2,"Couldn't resolve file \"",N,"\"")); return NULL; } R=openfile(&New,&F); if (!R) { WarnOld(cat3(2,"Couldn't open file \"",N,"\"")); return NULL; } setpath(&F); T=filetocharpointer(); closefile(&F); if (!T) { WarnOld(cat3(2,"Couldn't read file \"",N,"\"")); return NULL; } U=T; P=parsetextpoly(&U); old(T); if (!P) { WarnOld(cat3(2,"Couldn't read polygon in file \"",N,"\"")); return NULL; } VP=longtovectorpoly(P); release(P); old(N); return VP; } vectorpolys **ParsePolygons(const char *S, const FSRef *FSR) { FSRef New; file F; bool R; polygons **Py; vectorpolys **VP; char *N,*T; if (!S || !FSR) return NULL; N=remquotes(S); if (!N) return NULL; if (!N) { Warning("filename missing in field"); return NULL; } R=relfsref(FSR,N,&New); if (!R) { WarnOld(cat3(2,"Couldn't resolve file \"",N,"\"")); return NULL; } R=openfile(&New,&F); if (!R) { WarnOld(cat3(2,"Couldn't open file \"",N,"\"")); return NULL; } setpath(&F); T=filetocharpointer(); closefile(&F); if (!T) { WarnOld(cat3(2,"Couldn't read file \"",N,"\"")); return NULL; } Py=newpolys(); R=parsetextpolys(T,Py); old(T); if (!R) { WarnOld(cat3(2,"Error in polygons file \"",N,"\"")); return NULL; } VP=longtovectorpolys(Py); oldpolys(Py); return VP; } // Pictures FSRef *NewPictureReference(const char *S, const FSRef *FSR) { char *N; bool R; file F; FSRef New,*Pict; if (!S || !FSR) return NULL; N=remquotes(S); if (!N) return NULL; R=relfsref(FSR,N,&New); if (!R) { WarnOld(cat3(2,"Couldn't resolve file \"",N,"\"")); return NULL; } R=openfile(&New,&F); if (!R) { WarnOld(cat3(2,"Couldn't find file \"",N,"\"")); return NULL; } closefile(&F); old(N); Pict=pointer(sizeof(FSRef)); *Pict=F.fsref; return Pict; } picture LoadImage(const char *S, const FSRef *FSR) { char *N; bool R; file F; FSRef New; if (!S || !FSR) return NULL; N=remquotes(S); if (!N) return NULL; R=relfsref(FSR,N,&New); if (!R) { WarnOld(cat3(2,"Couldn't resolve file \"",N,"\"")); return NULL; } R=openfile(&New,&F); if (!R) { WarnOld(cat3(2,"Couldn't find file \"",N,"\"")); return NULL; } closefile(&F); old(N); return filepicture(&New); } // Initialisation void InitTTTFields(void) { initstringlist(&UFList); } void PurgeTTTFields(void) { purgestringlist(&UFList); }