HDARC: Sicherungskopien mal ganz unkompliziert

Sicherungskopien müssen sein. Doch was die Backup-Programme da teilweise an Anforderungen an den Benutzer stellen, hat wenig mit Bedienungsfreundlichkeit zu tun. Hier setzt HDARC an. Auf möglichst einfache Art und Weise werden Daten auf Disketten gesichert.

HDARC legt ein genaues Abbild der zu sichernden Struktur auf einer oder mehreren Disketten (je nach Platzbedarf) an. Der Aufbau der Ordnerstruktur wird während des Sicherungsvorganges automatisch auf der Diskette nachgebildet. Dateien werden nur komplett geschrieben, also nicht über mehrere Disketten verteilt. Es wird nicht komprimiert! Das alles führt dazu, daß ein beliebiges File auf einer Sicherungsdiskette jederzeit ohne Zusatzprogramm sofort restored werden kann. Als Auswahlkriterium für das Backup kann der Zustand des Archiv-Bits genutzt werden. Das ist natürlich erst ab TOS 1.04 sinnvoll. Automatik-Files können Informationen über zu sichernde Pfade enthalten. Ein Autostart-File macht das gleiche ohne weitere Einflußnahme durch den Nutzer.

Nach dem Start des Programmes wird der Benutzer zuerst gefragt, ob er das Archiv-Bit als Entscheidungsmerkmal für die Sicherung nutzen will. Bei einer ersten Sicherung sollte sinnigerweise darauf verzichtet werden, um einen kompletten Datensatz zu erhalten. Später sollte hier „ja“ angeklickt werden. Die nächste Frage ermittelt, ob während des Sicherungsvorganges das Archiv-Bit jeder gesicherten Datei gemäß der TOS-üblichen Konvention geändert werden soll. Eigentlich sollte hier immer mit „ja“ geantwortet werden. Jetzt versucht HDARC in seinem Verzeichnis eine Datei „FIDARCA.INF“ zu finden. Glückt dieser Versuch, liest das Programm die weiteren Informationen aus dieser Datei aus. Ist die Datei nicht zu finden, wird die Fileselectorbox angezeigt. Flier klickt der Nutzer entweder eine Datei mit dem Namen „HDARC.INF“ an oder verzweigt bis in den Pfad, ab dem alles gemäß Einstellung (s.o.) gesichert wird, und klickt „OK“. Das Verhalten bei „HDARC.INF“ ist das gleiche wie bei „HDARCA.INF“, mit dem kleinen Unterschied, daß es zwar in verschiedenen Pfaden mehrere Dateien des ersten Typs, aber insgesamt sinnvollerweise nur eine Datei des zweiten Typs geben kann. In den Automatik-Files lassen sich mehrere zu sichernde Pfade mit ihren Files unterbringen. Zur Laufzeit läßt sich über die Pfadauswahl nur ein Pfad sichern. Das Programm fragt nun nach der entsprechenden Diskette für die Sicherung. Überschreiben vorhandener Files auf der Diskette wird sicherheitshalber abgefragt. Was gesichert wird und welche Pfade gerade aufgebaut werden, wird ständig angezeigt. Ist die Diskette voll, fordert der Rechner eine neue an. Nach jeder gesicherten Pfadstruktur wird für die nächste Struktur unter Nennung ihres Namens eine neue Diskette angefordert.

Nun zum Sourcecode. Wie unschwer zu erkennen ist, entstand das Programm in C. Genauer gesagt habe ich den neuen Lattice C-Compiler benutzt.

main

Nach Initialisierung von AES wird fast der gesamte verbleibende Speicher zum Kopieren reserviert.

auswahl

Der gesamte Pfadauswahl-Dialog läuft hier ab. Zuerst erfolgt in anfrage die Anfrage bezüglich des Archiv-Bits, danach die Suche nach „HDARCA.INF“. Ist dieses File nicht vorhanden, wird der Fileselector gezeigt. Möglich ist hier die Auswahl des Files „HDARC.INF“ oder eines Pfades. Beide Automatik-Files werden in den String mfo übertragen und über zende vorbereitet für die Übergabe an zerlege. Auch die manuelle Pfadauswahl mündet schließlich in diesem Programmteil.

zerlege

Der angegebene Pfad wird hier zerlegt um die Ordnerstruktur auf der Diskette nachzubilden. Die Aufforderung zur Bereitstellung der entsprechenden Diskette wird dargestellt. Die Struktur ofeld spielt hier die zentrale Rolle. In ihr wird Ordner für Ordner einzeln abgelegt. Auch bei weiterer Verschachtelung während des Programmlaufes nimmt sie alle Ordnernamen auf. Die Nutzung der Befehle aus dem Lattice-Paket bietet sich hier zwar an, leider sind die Funktionen jedoch teilweise nicht voll funktionstüchtig. Herr Tüting vom CCD hat jedoch Abhilfe in Aussicht gestellt. Solange muß man also mit dem etwas umfangreicheren Source-Text vorliebnehmen.

suchen

Viele Wege führen zum Ziel. Ich habe mich hier für eine Rekursion entschieden. Nicht der einfachste Weg, wie verschiedene Artikel in Zeitschriften gezeigt haben, aber meiner Meinung nach der für dieses Problem sinnvollste. Ausgehend vom vorgegebenen Ordner, werden zuerst Files gesucht. Wenn keine mehr da sind, werden Ordner gesucht und jeder Ordner wieder zuerst nach Files, dann nach Ordnern usw. durchsucht. Wird ein File gefunden, wird nach bitcheck verzweigt, um zu sichern. Wird ein Ordner gefunden, wird nach struerw verzweigt, um die interne Ordnerstruktur zu erweitern und dann wieder suchen neu aufzurufen - rekursiv Vorarbeiten!

bitcheck

Hier wird die Vorgabe aus dem Anfangsdialog bzgl. des Archiv-Bits auf das File bezogen getestet. Entsprechend wird bei notwendiger Sicherung die noch nicht vorhandene Ordnerstruktur auf der Diskette erzeugt. Die Ordner werden nur erzeugt, wenn sie entweder mit einem File oder einem weiteren Ordner gefüllt werden. Letztendlich muß der letzte Ordner immer mit einem File gefüllt sein. Dieser Programmteil verzweigt nun zu arbeiten, was schließlich das Übertragen des Files erledigt. Dieser Programmteil ließe sich wahrscheinlich noch sehr weit ausbauen. Man denke hier an Proben auf Datum, Größe, Extension u.a.

struerw

ofeld wird erweitert. Die Struktur, die bereits in zerlege erzeugt wurde, wird hier wie ein Logbuch weitergeführt. Von hier ruft man wieder suchen auf. Wird suchen abgeschlossen (nicht verzweigt!), kehrt der Programmzeiger hierhin zurück und die Struktur ofeld wird wieder verkürzt, denn in dem letzten Ordner gab es nichts mehr zu finden.

arbeiten

Der entscheidende Programmteil! Hier findet die Verschiebung der Daten von der Platte auf die Diskette statt. Möglichst in einem Rutsch wird das ganze File in den Rechnerspeicher übertragen. Den notwendigen Speicherplatz haben wir bereits in main reserviert. Aber auch für zerstückelte Übertragung ist Vorsorge getroffen. Nur dann dauert es natürlich länger. Sollte das aktuelle File nicht mehr voll auf die Diskette passen, wird der schon geschriebene Teil gelöscht und eine neue Diskette angefordert. In diesem Fall sorgt diskchange für den Dialog und den Aufbau der Ordnerstruktur auf der neuen Diskette. Zuletzt wird gemäß Vorgabe (in Flag ar2) das Archiv-Bit des Ursprungs-Files geändert.

Nochmals in aller Deutlichkeit: Das Programm soll nicht Unmengen an Möglichkeiten bieten. Es ist dazu da, den Nutzer fast unbemerkt bei der Datensicherung zu unterstützen. Das wird besonders einfach bei Einrichtung von Automatik-Files („HDARCA.INF“ und „HDARC.INF“). Diese Files sind einfach nur Aufreihungen von Pfadangaben: z.B.

C:\AUTOW
D:\WICHTIG.DAT\BESOND.WIC\*.*
E:\SPIELE\FARBE\SIERRAW

Pro Zeile bitte nur ein Pfad und mit Return abschließen. In der ersten Pfadangabe würden jetzt alle Files aus dem AUTO-Ordner und alle Files von Ordnern, die im AUTO-Ordner liegen, gesichert werden. Jeder beliebige Texteditor, der ASCII-Texte erzeugt sollte ausreichen, um die Files zu erzeugen.


/* Name: HDArc
   Autor: Thomas Sturm
   (c) Maxon Computer GmbH 1993 */

#include <types.h>
#include <osbind.h>
#include <stat.h>
#include <stdio.h>
#include <string.h>
#include <aes.h>
#include <dos.h>

#define PATHSEP '\\'

char *buffer;
char ofeld[50][20];
int i = 0;
int k = 0;
long count;
int ar1 = 0;
int ar2 = 0;

/* Pfadstruktur erweitern */

void struerw(char *pfad, char datei[14])
{
    char *cp;
    char mdir[200];
    long error;
    char file [30] = "                     ";
    strcpy (mdir,pfad);
    strcpy (file,datei);
    /* Wenn ein neuer Ordner gefunden wird */
    /* dann wird er in ofeld abgelegt */
    strcpy (ofeld[++i],datei); 
    cp = strrchr(mdir, (int)(PATHSEP)); 
    strcpy (++cp,strcat (file,"\\*.*")); 
    suchen(mdir);
    /* Rekursion: In dem Pfad weitersuchen */ 
    if(k == i)
    {
        /* Nach Rückkehr eine Ordnerebene zurück */ 
        error = Dsetdrv(0); 
        error = Dsetpath("..\0"); 
        k--;
    }
    i--;
    if(i<k) k=i;
}

/* Untersucht auf Notwendigkeit zu sichern */

void bitcheck(int attr, char *dir, char *name, long size)
{
    int z;
    /* Wenn Bit 5 gesetzt ist, ist das Archivbit gesetzt; */
    /* d.h. die Datei ist nicht archiviert */ 
    if(((attr & 32) == 32) || (ar1 == 1))
    /* Bit 5 ? */
    {
        for(z=(k+1);z<=i;z++)
        {
            Dsetdrv(0);
            if(Dsetpath(ofeld[z]) != 0)
            {
                printf("\033E\nPfad %s wird angelegt\n",ofeld[z] ) ;
                Dcreate(ofeld[z));
                Dsetpath(ofeld[z]);
            }
        }
        k = i;
        arbeiten(dir,name,attr,size);
    }
}

/* Hier wird der mit dir übergebene Pfad nach Dateien */
/* und weiteren Ordnern durchsucht */

void suchen(char *dir)
{
    DMABUFFER dumb, *saved; 
    saved = {DMABÜFFER *)Fgetdta();
    Fsetdta(&dumb);
    /* Normale Dateien suchen */ 
    if(Fsfirst(dir, 0) == 0)
    {
        bitcheck((int)dumb.d_fattr, dir, dumb.d_fname, dumb.d_fsize); 
        while(Fsnext() == 0)
            bitcheck((int)dumb.d_fattr, dir, dumb.d_fname, dumb.d_fsize);
    }
    /* Keine weiteren Dateien in diesem Pfad.
       Deshalb Ordner suchen. Dabei ist Bit 4 = 16 
       Ordnerattribut und '.' Rückkehrordner */ 
    if(Fsfirst(dir,16) == 0)
        if ( (dumb.d_fattr == 16) && (dumb.d_fname[0] != '.'))
            struerw(dir,dumb.d_fname); 
        while(Fsnext() == 0)
            if((dumb.d_fattr -= 16) && (dumb.d_fname[0] != '.'))
                struerw(dir, dumb, d_fname) ;
    Fsetdta(saved);
}

/* Bei voller Diskette wird das unvollständige 
   File gelöscht und nach Diskettenwechsel ein 
   neuer Pfad aufgebaut */

void diskchange(long inhand,long outhand, 
                char *neuname,char *cp2, 
                char *pfad,
                char *cp,char *name,int type, 
                long status)
{
    Fclose((short)inhand);
    Fclose((short)outhand);
    printf("\033EDatei %s löschen\n",neuname);
    Fdelete(neuname);
    printf("\nDiskette wechseln\n");
    Cconin();
    Dsetdrv(0);
    strcpy (cp2,&pfad[3]);
    while (cp = strchr(cp2,(int)(PATHSEP)))
    {
        *cp++ = '\0';
        Dcreata(cp2);
        Dsetpath(cp2); 
        cp2 = cp;
    }
    inhand = Fopen(name,0); 
    printf("\033E\nKopiere %s\n",name); 
    outhand = Fcreate(neuname,type); 
    status = 0L;
}

/* Zeigt Fehlermeldung und terminiert Programm */

void fehler (short inhand, short outhand)
{
    printf("\nFehler bei Lesen oder Schreiben\n"); 
    Cconin();
    Fclose((short)inhand);
    Fclose((short)outhand);
    Mfree(buffer); 
    appl_exit();
    Pterm(1);
}

/* Bei einer Datei auf Diskette im vorher angelegten Ordner */

void arbeiten (char *pfad, char *datei, 
               int type, long menge)
{
    long inhand, outhand; 
    long status; 
    int res = 1; 
    char *cp;
    char name[200], neuname[200]; 
    char neupfad[200]; 
    char *cp2 = neupfad; 
    strcpy(name,pfad);
    cp = strrchr(name, (int)(PATHSEP)); 
    strcpy(++cp,datei);
    printf("\033E\nKopiere %s\n",name); 
    strcpy(neuname, name); 
    neuname[0] = 'A';
    if((outhand = Fopen(neuname,(short)(0))) > 0L)
    {
        res = form_alert(1,"[1][File existiert auf Disk| |Überschreiben?][ja |nein]");
        Fclose((short)outhand);
    }
    if(res == 1) /* res ist mit 1 initialisiert */ 
    {
        inhand = Fopen(name,(short)(0)); 
        outhand = Fcreate(neuname,type);
                    /* Datei erzeugen */ 
        while(menge > count)
        {
        /* Datei lesen */
            if(Fread((short)inhand,count, 
            buffer) < 0) 
                fehler((short)inhand,(short)outhand);
        /* Datei schreiben */
            if ((status = Fwrite((short)outhand, count,buffer)) < 0) 
                fehler((short)inhand,(short)outhand); 
            if(Status != count) 
                diskchange(inhand, outhand,neuname, cp2, pfad, cp, name, type, Status); menge -= status;
/* ... notfalls auch mehrmals */
        }
        while(menge >0) /* Noch Reste ? */
        {
            /* Rest lesen */ 
            if(Fread((short)inhand,menge, buffer) < 0) 
                fehler((short)inhand, (short)outhand);
            /* Rest schreiben */ 
            if((status = Fwrite((short)outhand, menge,buffer)) < 0) 
                fehler((short)inhand,(short)outhand); 
            if(Status != menge) 
                diskchange(inhand, outhand, neuname, cp2, pfad, cp, name, type, status); 
            menge -= status;
        }
        Fclose((short)inhand);
        Fclose((short)outhand);
        if (ar2 == 0) /* Bit 5 (Archiv) löschen */ 
        Fattrib(name,(short)1,(short)(type & 31));
    }
}

void zerlege(char *pfbez)
/* Das ofeld muß befüllt werden, */
/* dazu muß der Pfad zerlegt werden */
{
    char *cp1, *cp2;
    char alerttxt[200] = "[2][Diskette für die Sicherung | des nächsten Pfades | eingelegt ? ][OK]";
    /* Ersten Pfadsep. suchen */
    printf("\033E\n\n\nDer nächste Pfad ist\n%s\n",pfbez);
    i = 0;
    k = 0; /* Zähler für ofeld zurücksetzen */
    cp1 = strchr(pfbez,(int)(PATHSEP)); 
    do 
    {
    /* Zweiter Pfadsep. */
        cp2 = strchr(++cp1,(int)(PATHSEP)); 
        if (cp2) /*Falls Suche erfolglos-->cp2==0*/ 
        {
            *cp2 = '\0' ; /* Künstliches Stringende */ 
            strcpy(ofeld[++i],cp1);
            *cp2 = PATHSEP; /* Wieder Normalzustand */
            /* Altes Ende ist neuer Anfang für Suche */ 
            cp1 = cp2;
        }
    } while(cp2);
    form_alert(1, alerttxt)
/* Zwingt zum Updaten der Gemdos-Strukturen */ 
    Fsfirst("A:\\*.*",16);
    Dsetdrv(0);
/* GEMDOS ins Wurzelverzeichnis von A setzen */ 
    Dsetpath("\\"); 
    suchen(pfbez);
}

/* Handling des Archivbits abfragen */ 
void anfrage(void!
{
    int res;
    res = form_alert(1,"[2][Soll das Archivbit|beachtet|    werden ?][ja |nein]"); 
    if (res == 2) ar1 = 1; res = form alert(1, "[2] [Soll das Archivbit| verändert | werden ?][ja|nein]"); 
    if (res == 2) ar2 = 1;
}

void zende(char info[])
{
    char *cp1, *cp2;
    /* In dos.h FMSIZE = 128 */ 
    char pfbez[FMSIZE] = "";
    cp1 = info;
    do = strchr(cp1,0x0D); /*Zeilenende suchen*/ 
        *cp2 = '\0';
        Strcpy(pfbez,cp1);
        ++cp2;
        cp1 = ++cp2; 
        zerlege(pfbez);
    } while (strchr(cp1,'\\'));
}

/* Der Nutzer kann hier den Startpfad eingeben */ 
void auswahl(void)
{
    long fhandle; 
    char *cp1;
    /* Nimmt das Hdarc.inf auf */
    char info[1000] = "";
    char pfbez[FMSIZE] = "C:\\*.*";
    char file[FNSIZE]  = "";
    char label[20]="Bitte Pfad wählen"; 
    short button; 
    int res; 
    anfrage();
    if((fhandle = Fopen("HDARCA.INF",(short)(0))) > 0)
    {
        /* Autostart-Automatik-File vorhanden */ 
        Fread((short)(fhandle),1000L,info); 
        Fclose((short)(fhandle)); 
        zende (info);
    }
    else
    {
        res = fsel_exinput(pfbez,file,&button, label);
        if(button) /* Benutzer wählte einen Pfad */
        {
            /* Automatik-File gewählt */ 
            if(!strcmp(file,"HDARC.INF"))
            {
                cp1 = strrchr(pfbez,(int)(PATHSEP)); 
                strcpy(++cp1,file); 
                if((fhandle = Fopen(pfbez, (short)(0))) <= 0)
                {
                    printf("\nFehler beim Öffnen von HDARC.INF\n"); 
                    Cconin();
                    Mfree(buffer); 
                    appl_exit();
                    Pterm(1);
                }
                Fread((short)(fhandle), 1000L, info);
                Fclose((short)(fhandle)); 
                zende (info);
            }
            else zerlege (pfbez);
        }
    }
}

void main(void)
{
    appl_init();
    buffer = (char *)Malloc(count=((long)Malloc(-1L)-8000L)); 
    auswahl();
    Mfree(buffer); 
    appl_exit();
}

HDARC5.PRG
.C [-b4 -r6 -d0 -m0 -rs -fm] 
.S [-m0]
=
C.O
LCG.LIB 
LC.LIB

Thomas Sturm
Aus: ST-Computer 05 / 1993, Seite 70

Links

Copyright-Bestimmungen: siehe Über diese Seite