#include #include #include "dfc.h" /* -- Konstantendefinitionen -------------------------------------------------*/ #define MAXLINE 256 /* Maximale Zeilenlaenge */ /* -- Typedefinitionen -------------------------------------------------------*/ typedef struct List { long no; /* Zeilennummer */ long offset; /* Dateiposition */ int key; /* Schluessel */ struct List *next; /* Nachstes Element */ } LIST; typedef struct Fileset { long no; /* Zeilennummer */ long offset; /* Dateiposition */ short int eof; /* EOF Anzeige */ char line[MAXLINE]; /* Letzte Zeile */ char buffer[MAXLINE]; /* Zeilenpuffer */ LIST *cp; /* Kettenanfang */ LIST *last; /* Kettenanende */ LIST *sync; /* Synchronisationszeile*/ FILE *fp; /* File-pointer */ } FILESET; /* -- Interne Funktionen -----------------------------------------------------*/ void Addln(), FreeSet(), Fseek(), Readln(), Release(), Report(); char *Fgets(); int Compare(), MakeKey(), Match(), Sync(); LIST *GetLink(); FILESET *GetSet(); /* -- Globale Daten ----------------------------------------------------------*/ static DFC_PAR *Dfc; /* Eingangsparameter */ static jmp_buf Dfc_jmp; /* Fuer Fehlerbehandlung */ static FILESET *Old; /* Daten der ersten Datei */ static FILESET *New; /* Daten der zweiten Datei */ /* -- dodfc -------------------------------------------------------------------- Dodfc fuehrt differentiellen Dateivergleich aus. ---------------------------------------------------------------------------*/ int dodfc(dp) DFC_PAR *dp; { int status; if (! (status=setjmp(Dfc_jmp)) ) { Old=GetSet(); New=GetSet(); Old->fp=dp->fp_old; New->fp=dp->fp_new; Dfc=dp; status=Compare(); } FreeSet(Old); FreeSet(New); return(status); } /* -- GetSet ------------------------------------------------------------------- GetSet besorgt den Speicherplatz fuer ein Fileset. Falls nicht genug Speicherplatz verfuegbar ist, wird der Dateivergleich abgebrochen. ---------------------------------------------------------------------------*/ FILESET *GetSet() { FILESET *fs; if ( (fs=(FILESET *) calloc((unsigned)1,(unsigned)sizeof(FILESET) ))== (FILESET *)0){ longjmp(Dfc_jmp,NO_MEMORY); } fs->cp=fs->last=GetLink(); return(fs); } /* -- GetLink ------------------------------------------------------------------ GetLink besorgt den Speicherplatz fuer ein Listenelement. Falls nicht genug Speicherplatz verfuegbar ist, wird der Dateivergleich abgebrochen. ---------------------------------------------------------------------------*/ LIST *GetLink() { LIST *cp; if ( (cp=(LIST *) calloc((unsigned)1,(unsigned)sizeof(LIST) ))== (LIST *)0){ longjmp(Dfc_jmp,NO_MEMORY); } return(cp); } /* -- FreeSet ------------------------------------------------------------------ FreeSet gibt den dynamisch besorgten Speicherplatz eines Fileset (inclusive der enthaltenen Kette) frei. ---------------------------------------------------------------------------*/ void FreeSet(fs) FILESET *fs; { if (fs!=(FILESET *)0) { Release(fs->cp); free( (char *) fs); } } /* -- Release ------------------------------------------------------------------ Release gibt den dynamisch besorgten Speicherplatz einer Kette frei. ---------------------------------------------------------------------------*/ void Release(cp) LIST *cp; { LIST *p,*next; for (p=cp; p!=(LIST *)0; p=next) { next=p->next; free( (char *) p); } cp=(LIST *)0; } /* -- Compare ------------------------------------------------------------------ Compare besteht aus einem simplen Zeilenvergleich, der bei ungleichen Zeilen durch die innere Synchronisationsschleife unterbrochen wird. In der inneren Schleife wird fuer jede Datei die Kette der ungleichen Zeilen aufgebaut. ---------------------------------------------------------------------------*/ int Compare() { int dfcnt=NO_DIFFERENCE; Readln(Old); Readln(New); while (! Old->eof || ! New->eof) { if (strcmp(Old->line,New->line)) { dfcnt++; Old->last->key=MakeKey(Old->line); New->last->key=MakeKey(New->line); do { Addln(Old); Addln(New); } while (! Sync()); } Readln(Old); Readln(New); } return(dfcnt); } /* -- Readln ------------------------------------------------------------------- Readln liest eine Zeile, speichert zugehoerige Informationen, wie Offset und Zeilennummer. ---------------------------------------------------------------------------*/ void Readln(fs) FILESET *fs; { if (! fs->eof) { fs->last->offset=fs->offset; fs->eof=(Fgets(fs->line,MAXLINE,fs->fp)==NULL); fs->offset=ftell(fs->fp); fs->last->no=++fs->no; } } /* -- MakeKey ------------------------------------------------------------------ MakeKey erzeugt fuer eine Zeile einen Schluessel, der in der Matcg-Funktion zum "schnellen Zeilenvergleich" benutzt wird. ---------------------------------------------------------------------------*/ int MakeKey(line) char *line; { int key=0; while (*line) { key+=*line++; } return(key); } /* -- Addln -------------------------------------------------------------------- Addln verlaengert die Kette ungleicher Zeilen um eine weitere Zeile. ---------------------------------------------------------------------------*/ void Addln(fs) FILESET *fs; { if ( !fs->eof) { fs->last->next=GetLink(); fs->last=fs->last->next; Readln(fs); fs->last->key=MakeKey(fs->line); } } /* -- Fgets -------------------------------------------------------------------- Der Unterschied zur Bibliotheksfunktion liegt darin, dass beim EOF ein leeres Feld zurueckgeliefert wird, das (bei Unterschieden am Dateiende) fuer eine Synchronisation bei EOF sorgt. ---------------------------------------------------------------------------*/ char *Fgets(line,n,stream) char *line; FILE *stream; { if (fgets(line,n,stream)==NULL) { *line='\0'; /* !!! */ return(NULL); } return(line); } /* -- Sync --------------------------------------------------------------------- Sync setzt beide Dateien auf die naechste Leseposition. Im Falle einer Syn- chronisation werden die Ketten, die fuer ungleiche Zeilen aufgebaut wurden freigegeben. ---------------------------------------------------------------------------*/ int Sync() { int status; if ( (status=Match()) ) { Report(); Release(Old->cp); Release(New->cp); Old->cp=Old->last=GetLink(); New->cp=New->last=GetLink(); } Fseek(Old->fp,Old->offset); Fseek(New->fp,New->offset); return(status); /* !=0 bei Synchronisation */ } /* -- Fseek -------------------------------------------------------------------- Fseek stellt die mit 'fp' verbundene Datei auf die Position 'offset'. Im Fehlerfall wird der gesamte Dateivergleich beendet! ---------------------------------------------------------------------------*/ void Fseek(fp,offset) FILE *fp; long offset; { if (fseek(fp,offset,0)) { longjmp(Dfc_jmp,BAD_SEEK); } } /* -- Match -------------------------------------------------------------------- Die jeweils letzte Zeile der Kette ungleicher Zeilen wird mit allen Ketten- elementen der anderen Kette verglichen, bis gleiche Zeilen auftreten. Bei gleichen Zeilen kann synchronisiert werden. Als Erweiterung kann die Uebereinstimmung mehrerer aufeinanderfolgender Zei- len als Synchronisationskriterium gewaehlt werden. ---------------------------------------------------------------------------*/ int Match() { int i; FILESET *fsa,*fsb; for (i=1,fsa=Old,fsb=New; i<=2; i++,fsa=New,fsb=Old) { LIST *cp; for (cp=fsa->cp; cp!=(LIST *)0; cp=cp->next) { if (cp->key==fsb->last->key) { /* gleiche Schluessel */ Fseek(fsa->fp,cp->offset); Fgets(fsa->buffer,MAXLINE,fsa->fp); if (! strcmp(fsa->buffer,fsb->line)) { /* gleiche Zeilen */ short int matches; Fseek(fsb->fp,fsb->offset); for (matches=1; matchesmatches; matches++) { Fgets(fsa->buffer,MAXLINE,fsa->fp); Fgets(fsb->buffer,MAXLINE,fsb->fp); if (strcmp(fsa->buffer,fsb->buffer)) { break; /* Ungleiche Zeilen */ } } if (matches>=Dfc->matches) { /* Kann synchronisieren */ if (cp->next!=(LIST *)0) { fsa->offset=cp->next->offset; fsa->eof=0; } fsa->no=cp->no; fsa->sync=cp; /* Fuer Report */ fsb->sync=fsb->last; return(1); } } } } } return(0); } /* -- Report ------------------------------------------------------------------- Report zeigt die gefundenen Differenzen an. ---------------------------------------------------------------------------*/ void Report() { long oldcnt,newcnt; long i; oldcnt=Old->sync->no - Old->cp->no; newcnt=New->sync->no - New->cp->no; if (oldcnt>0 && newcnt>0) { fprintf(Dfc->fp_out,"%ld,%ldc%ld,%ld\n",Old->cp->no,Old->sync->no-1, New->cp->no,New->sync->no-1); } else if (oldcnt>0) { fprintf(Dfc->fp_out,"%ld,%ldd\n",Old->cp->no,Old->sync->no-1); } else { fprintf(Dfc->fp_out,"%lda%ld,%ld\n",Old->cp->no,New->cp->no, New->sync->no-1); } if (Dfc->style==LISTING) { if (oldcnt>0) { /* Zeile aus Oldfile */ Fseek(Old->fp,Old->cp->offset); for (i=1; i<=oldcnt; i++) { Fgets(Old->buffer,MAXLINE,Old->fp); fprintf(Dfc->fp_out,"< %s",Old->buffer); } } if (oldcnt>0 && newcnt>0) { fprintf(Dfc->fp_out,"---\n"); } } if (newcnt>0) { /* Zeile aus Newfile */ Fseek(New->fp,New->cp->offset); for (i=1; i<=newcnt; i++) { Fgets(New->buffer,MAXLINE,New->fp); fprintf(Dfc->fp_out,"> %s",New->buffer); } } }