// twhat.c // // by John D. de Boer #include "twist.h" extern bool Verbose; int EVENTS; // number of historical events Event ***Events; // the events static stringlist UEList; // event names that were not found // bool VoidIsEvent(const void *V) { return V && ((Event *)V)->leg=='EVNT'; } // Allocation void InitEvents(void) { EVENTS=0; Events=(Event ***)handle(0); initstringlist(&UEList); } Event *NewEvent(bool AddToList) { Event *R; R=pointer(sizeof(Event)); if (AddToList) { minimumsize(Events,EVENTS + 1,sizeof(Event *)); (*Events)[EVENTS++]=R; } R->leg='EVNT'; R->name=R->info=R->ref=R->src=R->cap=R->aka=NULL; R->followed=0; R->ntype=0; R->inbnd.num=0; R->inbnd.link=NULL; InitSpan(&(R->date)); R->place=NULL; R->pers=NULL; R->persref=NULL; R->trav=NULL; R->pict=NULL; return R; } static void OldEvent(Event *E) { if (!E) return; old(E->name); old(E->info); old(E->ref); old(E->src); OldTravels(E->trav); release(E->inbnd.link); old(E); } void PurgeEvents(void) { while (EVENTS>0) OldEvent((*Events)[--EVENTS]); purgestringlist(&UEList); } void AddType(Event *E, int TYPE) { if (!E || E->ntype>=MAXOCC || TYPE<0) return; E->type[E->ntype++]=TYPE; } // Resolving references to events static void UnknownEvent(const char *S) { if (stringisinlist(&UEList,S)) return; NoteOld(cat(0,"Unresolved reference to an event: ",(char *)S)); addstringtolist(&UEList,copyof(S)); } static void CircularEventLink(const Event *E, const char *S) { SetItemFileName(E); NoteOld(cat(0,"Self-referential link: ",(char *)S)); } static bool NameMatchesEvent(const char *S, const char *E) { if (!S || !*S || !E || !*E) return 0; if (begins(E,"the ") || begins(E,"The ")) { E+=4; if (begins(S,"the ") || begins(S,"The ")) S+=4; } return streq(S,E); } static Event *FindEvt(const char *S, const Span *L) { int i; Event *E; if (!S || !*S) return NULL; for (i=0;iname) && !NameMatchesEvent(S,E->aka)) continue; if (!L || SpansOverlap(L,&(E->date),0)) return E; } return NULL; } Event *FindEvent(const char *S, const Span *L, bool GenErr, const void *From) { Event *E; if (!S) return NULL; if (L) { E=FindEvt(S,L); if (E) { if (E==From) CircularEventLink(E,S); return E; } } E=FindEvt(S,NULL); if (E || !GenErr) return E; SetItemFileName(From); UnknownEvent(S); return NULL; } // Checking new events static void CheckForDuplicateEvent(Event *E) { Event *Dup; bool Overlap; if (!E) return; Dup=FindEvent(E->name,NULL/*&(E->date)*/,0,NULL); if (!Dup || Dup==E) return; //E->ambig=1; Dup->ambig=1; if (E->aka && Dup->aka && !streq(E->aka,Dup->aka)) return; if (E->aka && !Dup->aka) return; if (Dup->aka && !E->aka) return; Overlap=SpansOverlap(&(E->date),&(Dup->date),1); if (!Overlap) WarnOld(cat4(0,"Similarly-named events: ",Dup->name,", ",E->name)); else WarnOld(cat4(0,"Possible duplicate event: ",Dup->name,", ",E->name)); } void VerifyNewEvent(Event *E) { Span *D; if (!E) return; D=&(E->date); if (!E->name) { Warning("Event with no name"); return; } if (E->ntype<1) AddType(E,ParseType("misc")); if (E->ntype<1) WarnOld(cat3(0,"Event \"",E->name,"\" has missing or invalid type")); if (D->start.kind==timeInval) WarnOld(cat3(0,"Date of event \"",E->name,"\" is missing or has bad format")); if (D->end.kind==timeInval) D->end=D->start; if (D->start.kind==timeJD && D->end.kind==timeJD && D->start.jd>D->end.jd) DangerOld(cat3(0,"Start and end dates of event \"",E->name,"\" are in wrong order")); if (!E->place && E->pers) E->place=PersonLocation(E->pers,&(D->start)); //if (!E->place) WarnOld(cat3(0,"Event \"",E->name,"\" has no valid location")); if (E->trav) VerifyTravels(E->trav,D,E->name); PlaceReference(E->place,D); if (Verbose) CheckForDuplicateEvent(E); } // Database delta void CheckEventDelta(Event *E) { Event *Ex; if (!E) return; if (E->date.end.kind==timeInval) E->date.end=E->date.start; Ex=FindEvent(E->name,NULL,0,NULL); if (Ex) { if (!DatesEqual(&(E->date.start),&(Ex->date.start))) NoteOld(cat(0,"Start date changed for ",E->name)); if (!DatesEqual(&(E->date.end),&(Ex->date.end))) NoteOld(cat(0,"End date changed for ",E->name)); if (!streq(E->aka,Ex->aka)) NoteOld(cat3(0,"Alternate name of '",E->name,"' changed")); if (abso(length(E->info) - length(Ex->info)) > 5) NoteOld(cat3(0,"Info para for ",E->name," changed significantly")); //else if (!streq(E->info,Ex->info)) NoteOld(cat3(0,"Info para for '",E->name,"' changed")); if (E->place!=Ex->place) NoteOld(cat3(0,"Place of '",E->name,"' changed")); } else WarnOld(cat(0,"New event definition: ",E->name)); OldEvent(E); } // static bool HappenedEarlier(const void *A, const void *B) { Span *P,*Q; if (!A || !B) return 0; P=&(((Event *)A)->date); Q=&(((Event *)B)->date); if ((P->start.kind==timeInval)!=(Q->start.kind==timeInval)) return Q->start.kind==timeInval; return P->start.jdstart.jd; } void OrderEventsByDate(void) { lock(Events); sort(*Events,EVENTS,&HappenedEarlier); unlock(Events); } // Links from events to people void ResolveEventPerson(Event *E, bool GenErr) { if (!E || !E->persref) return; E->pers=FindPerson(E->persref,&(E->date),GenErr,E); if (E->pers) { old(E->persref); E->persref=NULL; } } void ResolveEventReferences(Event *E) { ResolveEventPerson(E,Verbose); CheckInfoText(E,copyof(E->info),&(E->date),Verbose); SpellCheck(E->name,1); SpellCheck(E->info,1); }