/******************************************************/
/* EasyBackup, Backup.c (c)1989 Oliver Enseling       */
/******************************************************/

extern void     EndBackup(), UserRequest();



#include "support.c"

long            CompDate[3];           /* DateStamp zum vergleichen */
int             Drive;                 /* Nummer des selektierten
                                        * Disk-Drives */
char           *Source;                /* Source-Directorypath */
char            Buffer[FMSIZE];        /* Hilfspuffer zum
                                        * "Zusammenbau" des ganzen
                                        * filenamens */
char            DirPath[FMSIZE] = "";  /* aktueller Directorypath */
BOOL            FoundInter = FALSE, FoundArchiviere = FALSE, FoundArchivstatus = FALSE;
FILE           *ListFile = NULL;

/* Variablen */

LONG            TrackNr, ByteNr;

/* NewDisk: neue Diskette anfordern */

LONG            DiskNr;
void
NewDisk()
{
  BOOL            diskcorrect;
  char            buf[5];

  TrackDiskBlock->iotd_Req.io_Length = 0;       /* Drive-Motor aus */
  DeviceCommand(TrackDiskBlock, (UWORD) TD_MOTOR);
  DiskNr++;
  do {
    setmem(buf, 5, 0);
    Beep(880, 25, 64);
#ifdef DEUTSCH
    printf("Neue Kopie-Diskette #%d in Laufwerk DF%d: einlegen\n", DiskNr,
           Drive);
#else
    printf("Insert new backup disk #%d in drive DF%d:", DiskNr, Drive);
#endif
    WaitDisk(TrackDiskBlock);
    ReadTrack(TrackDiskBlock,
              &TrackBuffer[TRACK_SIZE], LabelBuffer, 0);
    strncpy(buf, &TrackBuffer[TRACK_SIZE], 4);
    if (buf[0] != 'B')
      diskcorrect = TRUE;
    else {
      int             dnr = atoi(buf + 1);

      if (dnr < DiskNr && dnr > 0) {
#ifdef DEUTSCH
        printf("Achtung Kopie-Diskette #%d wird überschrieben !!!\n", dnr);
        printf("Diskette wechseln ? ");
#else
        printf("Old backup disk #%d is being overwritten\n", dnr);
        printf("Change disk ? ");
#endif
        diskcorrect = FileAbfrage();
      } else
        diskcorrect = TRUE;
    }
  } while (!diskcorrect);
#ifdef DEUTSCH
  printf("Diskette OK\n");
#else
  printf("Disk OK\n");
#endif
  sprintf(TrackBuffer, "B%d%d%d", DiskNr / 100 % 10, DiskNr / 10 % 10,
          DiskNr % 10);
  setmem(&TrackBuffer[4], 4, 0xff);
  TrackNr = 0;
  ByteNr = 0;
  PrintDisk(DiskNr);
  PrintTrack(0);
}

/* FormatTrack: einen Track formatieren */

void
FormatTrack(db, nr, buf)
    struct IOExtTD *db;
    LONG            nr;
    BYTE           *buf;
{
  PrintTrack((nr + 1) % MAX_TRACKS);
  FOREVER
  {
    DeviceCommand(db, (UWORD) TD_CHANGENUM);
    db->iotd_Count = db->iotd_Req.io_Actual;
    db->iotd_Req.io_Data = (APTR) buf;
    db->iotd_Req.io_Length = (ULONG) TRACK_SIZE;
    db->iotd_Req.io_Offset = nr * TRACK_SIZE;
    DeviceCommand(db, (UWORD) ETD_FORMAT);
    if (db->iotd_Req.io_Error) {
#ifdef DEUTSCH
      printf("Fehler beim Schreiben auf Spur #%d:\n%s\n",
             nr, TrackDiskError((LONG) db->iotd_Req.io_Error));
#else
      printf("Error writing track #%d:\n%s\n",
             nr, TrackDiskError((LONG) db->iotd_Req.io_Error));
#endif
      UserRequest();
    } else
      break;
  }
}

/* RawPutByte: ein Byte in ein Backup schreiben */

void
RawPutByte(b)
    BYTE            b;
{
  ByteNr++;
  if (ByteNr >= TRACK_SIZE) {
    TrackNr++;
    if (TrackNr >= MAX_TRACKS) {
#ifdef DEUTSCH
      printf("Diskette voll\n");
#else
      printf("Disk full\n");
#endif
      NewDisk();
    } else if (TrackNr != MAX_TRACKS - 1)
      ByteNr = 0;
    else {
      strcpy(&TrackBuffer[8], DirPath);
      ByteNr = strlen(&TrackBuffer[8]) + 1 + 8;
    }
    FormatTrack(TrackDiskBlock, TrackNr, TrackBuffer);
  }
  TrackBuffer[ByteNr] = b;
}

/* Bytes mit Dateiendemarkierung kodieren */

void
EncodeByte(b)
    char            b;
{
  if (b == DATEIENDEMARKE) {
    RawPutByte(DATEIENDEMARKE);
    RawPutByte(DATEIENDEMARKE);
  } else
    RawPutByte(b);
}

/* RawWrite: Anzahl Bytes auf das Backup schreiben */

void
RawWrite(buf, len)
    BYTE           *buf;
    LONG            len;
{
  LONG            i;

  for (i = 0; i < len; i++)
    EncodeByte(buf[i]);
}

/*
 * ByteCount: Anzahl der aufeinanderfolgenden gleichen Bytes
 * ermitteln
 */

LONG
ByteCount(begin, max)
    UBYTE          *begin, *max;
{
  int             count;
  UBYTE           b;

  b = *begin;
  for (count = 0L; count < 255L; count++)
    if ((begin[count] != b) || ((UBYTE *) (begin + count) > max))
      break;
  return (count);
}

/*
 * EndBackup: Backup beenden / letzten Buffer schreiben + Endmarke
 * setzen Drive-Motor ausschalten
 */

void
EndBackup()
{
  LONG            diskfull;

  diskfull = (TrackNr + 1) * TRACK_SIZE + ByteNr;
  RawPutByte(ID_ENDDIR);
  ByteNr = TRACK_SIZE - 1;
  RawPutByte((BYTE) - 1);
  ReadTrack(TrackDiskBlock, TrackBuffer, LabelBuffer, 0);
  memcpy(&TrackBuffer[4], (char *) &diskfull, 4);
  FormatTrack(TrackDiskBlock, 0, TrackBuffer);
  TrackDiskBlock->iotd_Req.io_Length = 0;       /* Drive-Motor aus */
  DeviceCommand(TrackDiskBlock, (UWORD) TD_MOTOR);
}

/*
 * BackupFile: ein File überprüfen, ggf. packen und auf die
 * BackupDisk schreiben
 */

BOOL
BackupFile(path, fib)
    char           *path;
    struct FileInfoBlock *fib;
{
  LONG            ssize = fib->fib_Size;
  int             file;
  char           *fn = fib->fib_FileName;

  ConcatPath(Buffer, Source, path);
  if (datecmp((long *) &fib->fib_Date, CompDate) < 0) {
    if (FoundArchivstatus && (fib->fib_Protection & FIBF_ARCHIVE))
      return (TRUE);
#ifdef DEUTSCH
    printf("Kopie für \033[33m%s\033[0m %d Bytes ... ", Buffer, fib->fib_Size);
#else
    printf("Backup for \033[33m%s\033[0m %d Bytes ... ", Buffer, fib->fib_Size);
#endif
    if (FoundInter)
      if (!FileAbfrage())
        return (TRUE);
    RawPutByte(ID_FILE);
    RawWrite(fn, strlen(fn) + 1);
    RawWrite((BYTE *) & fib->fib_Protection, sizeof(fib->fib_Protection));
    if (FoundArchiviere) {
      fib->fib_Protection |= FIBF_ARCHIVE;
      SetProtection(Buffer, fib->fib_Protection);
    }
    RawWrite((BYTE *) & fib->fib_Date, sizeof(fib->fib_Date));
    RawWrite(fib->fib_Comment, strlen(fib->fib_Comment) + 1);
    if (ListFile)
      fprintf(ListFile, "%8d%5d%5d %-51s%9d\n",
         DiskNr, (TrackNr + 1) / 2, (TrackNr + 1) % 2, path, ssize);
    if (ssize > 0) {
      if ((file = open(Buffer, O_RDONLY, 0)) != -1) {
        LONG            remain;

        for (remain = ssize; remain > BUFFER_SIZE; remain -= BUFFER_SIZE) {
          read(file, FileBuffer, BUFFER_SIZE);
          RawWrite(FileBuffer, BUFFER_SIZE);
        }
        read(file, FileBuffer, remain);
        RawWrite(FileBuffer, remain);
        close(file);
        printf("OK\n");
      } else {
#ifdef DEUTSCH
        printf("Lesefehler, DOS-Fehlernummer: %d\n", _OSERR);
#else
        poserr(Buffer);
#endif
        UserRequest();
      }
      RawPutByte(DATEIENDEMARKE);
      return ((BOOL) (file > 0));
    } else {
#ifdef DEUTSCH
      printf("Leere Datei --> 0 Bytes\n");
#else
      printf("Empty file --> 0 Bytes\n");
#endif
      RawPutByte(DATEIENDEMARKE);
      return (TRUE);
    }
  }
  return (TRUE);
}

/* ein Verzeichnis "backuppen" */

BOOL
BackupDir(path, fib)
    char           *path;
    struct FileInfoBlock *fib;
{
  char           *dn = fib->fib_FileName;

  if (datecmp((long *) &fib->fib_Date, CompDate) >= 0)
    return (FALSE);
  if (FoundArchivstatus && (fib->fib_Protection & FIBF_ARCHIVE))
    return (FALSE);
  strcpy(DirPath, path);
  ConcatPath(Buffer, Source, path);
  if (datecmp((long *) &fib->fib_Date, CompDate) < 0) {
#ifdef DEUTSCH
    printf("Verzeichnis \033[33;2m%s\033[0m", Buffer);
#else
    printf("Directory \033[33;2m%s\033[0m", Buffer);
#endif
    if (FoundInter) {
      if (!FileAbfrage())
        return (FALSE);
    } else
      printf("\n");
    RawPutByte(ID_DIR);
    RawWrite(dn, strlen(dn) + 1);
    return (TRUE);
  } else
    return (FALSE);
}

/* BackupEndDir: ENDE-Markierung für ein Verzeichnis "backuppen" */

void
BackupEndDir()
{
  RawPutByte(ID_ENDDIR);
}

/* BackupTree: Verzeichnisbaum rekursiv backuppen */

BOOL
BackupTree(s)
    char           *s;
{
  struct FileInfoBlock *fib;
  BOOL            ok = TRUE;
  LONG            err;
  char            path[FMSIZE];
  BPTR            lock;

  ConcatPath(Buffer, Source, s);
  if (fib = AllocMem(sizeof(struct FileInfoBlock), MEMF_CLEAR | MEMF_CHIP)) {
    if (lock = Lock(Buffer, ACCESS_READ)) {
      if (Examine(lock, fib)) {
        while (ok && ExNext(lock, fib)) {
          ConcatPath(path, s, fib->fib_FileName);
          if (fib->fib_DirEntryType > 0) {
            if (BackupDir(path, fib)) {
              ok = BackupTree(path);
              BackupEndDir();
            }
          } else
            ok = BackupFile(path, fib);
        }
      }
      UnLock(lock);
    }
    FreeMem(fib, sizeof(struct FileInfoBlock));
  } else {
    ok = FALSE;
#ifdef DEUTSCH
    printf("Nicht genügend Speicher für den FileInfoBlock !!!\n");
#else
    printf("Not enough memory for FileInfoBlock\n");
#endif
  }
  if (err = IoErr())
    if (!(ok = (err == ERROR_NO_MORE_ENTRIES))) {
#ifdef DEUTSCH
      printf("Fehler beim Lesen des Verzeichnisses.\n"
             "DOS-Fehlernummer:%4d\n", err);
#else
      printf("Error reading directory.\n"
             "DOS-Error:%4d\n", err);
#endif
      UserRequest();
    }
  return (ok);
}

/* Umwandlung von String nach DateStamp */

BYTE            MonthDays[12] =
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
LONG            LeapYears = 0x44444444;
BOOL
StrToDs(s, ds)
    char           *s;
    struct DateStamp *ds;
{
  int             i, d = atoi(s), m = atoi(&s[3]), y = atoi(&s[6]) - 78;

  if ((d > 0) && (m > 0) && (y >= 0)) {
    for (i = 0; i < y; i++)
      ds->ds_Days += LeapYears & (1 << i) ? 366 : 365;
    for (i = 0; i < m - 1; i++)
      ds->ds_Days += MonthDays[i];
    ds->ds_Days += d - 1 + ((LeapYears & (1 << y)) && (m > 2) ? 1 : 0);
    return (TRUE);
  }
  return (FALSE);
}

/* Hauptprogramm: Parameter parsen, Error-Messages ausgeben */

void
main(argc, argv)
    int             argc;
    char          **argv;
{
  BOOL            foundfrom = FALSE, foundto = FALSE, foundsince = FALSE, foundappend = FALSE,
                  foundliste = FALSE;
  char           *dest, *date, *listname;
  int             i;
  FILE           *file;

  printf("EasyBackup V1.0, Backup  ©1990 Oliver Enseling\n\n");
  for (i = 1; i < argc; i++) {
#ifdef DEUTSCH
    if (stricmp(argv[i], "VON") == 0)
#else
    if (stricmp(argv[i], "FROM") == 0)
#endif
    {
      foundfrom = TRUE;
      i++;
      Source = argv[i];
    } else {
#ifdef DEUTSCH
      if (stricmp(argv[i], "NACH") == 0)
#else
      if (stricmp(argv[i], "TO") == 0)
#endif
      {
        foundto = TRUE;
        i++;
        dest = argv[i];
      } else {
#ifdef DEUTSCH
        if (stricmp(argv[i], "SEIT") == 0)
#else
        if (stricmp(argv[i], "SINCE") == 0)
#endif
        {
          foundsince = TRUE;
          i++;
          date = argv[i];
        } else {
#ifdef DEUTSCH
          if (stricmp(argv[i], "ERWEITERN") == 0)
#else
          if (stricmp(argv[i], "APPEND") == 0)
#endif
              {
            foundappend = TRUE;
            i++;
            DiskNr = atoi(argv[i]);
            if (DiskNr < 1)
              DiskNr = 1;
          } else {
#ifdef DEUTSCH
            if (stricmp(argv[i], "INTERAKTIV") == 0)
#else
            if (stricmp(argv[i], "QUERY") == 0)
#endif
            {
              FoundInter = TRUE;
            } else {
#ifdef DEUTSCH
              if (stricmp(argv[i], "LISTE") == 0)
#else
              if (stricmp(argv[i], "LIST") == 0)
#endif
              {
                foundliste = TRUE;
                i++;
                listname = argv[i];
              } else {
#ifdef DEUTSCH
                if (stricmp(argv[i], "ARCHIVIERE") == 0)
#else
                if (stricmp(argv[i], "ARCHIVATE") == 0)
#endif
                {
                  FoundArchiviere = TRUE;
                } else {
#ifdef DEUTSCH
                  if (stricmp(argv[i], "ARCHIVSTATUS") == 0)
#else
                  if (stricmp(argv[i], "CHECKARCHIVE") == 0)
#endif
                  {
                    FoundArchivstatus = TRUE;
                  } else {
                    if (!foundfrom) {
                      Source = argv[i];
                      foundfrom = TRUE;
                    } else {
                      if (!foundto) {
                        dest = argv[i];
                        foundto = TRUE;
                      }
#ifdef DEUTSCH
                      else
                        printf("Parameter \033[32m%s\033[31m wird ignoriert !\n",
                               argv[i]);
#else
                      else
                        printf("Argument \033[32m%s\033[31m ignored\n", argv[i]);
#endif
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  if (!(foundfrom && foundto)) {
#ifdef DEUTSCH
    printf("Syntax: Backup [VON] <ver> [NACH] <laufwerk> [SEIT <datum>|ZULETZT]\n"
           "               [ERWEITERN <disk>] [INTERAKTIV] [LISTE <listfile>]\n"
           "               [ARCHIVIERE] [ARCHIVSTATUS]\n"
           "       ver ------ Das zu kopierende Verzeichnis\n"
           "       laufwerk - Das Laufwerk mit dem gearbeitet wird\n"
           "       datum ---- Das Datum von dem aus alle zu einem späteren\n"
           "                  Zeitpunkt erstellten oder veränderten Dateien\n"
         "                  kopiert werden, das Format: DD MM YY.\n"
      "                  Es kann ZULETZT eingegeben werden um das\n"
      "                  Datum des letzten Aufrufes von BACKUP zu\n"
           "                  verwenden.\n"
    "       disk ----- Die Nummer der Kopie-Diskette, ab der eine\n"
           "                  Sichertheitskopie erweitert werden soll, wenn\n"
           "                  die ERWEITERN-Option benutzt wird\n"
    "       listfile - Name der Datei für das Backup-Verzeichniss\n"
           "\n"
           "       Die Schlüsselwörter VON und NACH können weggelassen werden.\n");
#else
    printf("Usage: Backup [FROM] <dir> [TO] <drive> [SINCE <date>|LAST]\n"
       "              [APPEND <disknr>] [QUERY] [LIST <listfile>]\n"
           "     dir ------ Directory, which is being backed up\n"
           "     drive ---- Disk-drive for backup-disks\n"
           "     date ----- only files created later than <date> are being\n"
           "                backed up\n"
    "     disknr --- Number of disk to start an incremental backup\n"
           "     listfile - Filename for listing file\n");
#endif
           CloseAll(1);
  }
  Drive = -1;
  if (stricmp("DF0:", dest) == 0)
    Drive = 0;
  if (stricmp("DF1:", dest) == 0)
    Drive = 1;
  if (stricmp("DF2:", dest) == 0)
    Drive = 2;
  if (stricmp("DF3:", dest) == 0)
    Drive = 3;
  if (Drive < 0) {
#ifdef DEUTSCH
    printf("Kein Laufwerk %s\n", dest);
#else
    printf("Not a disk drive %s\n", dest);
#endif
    CloseAll(5);
  }
  if (foundsince) {
#ifdef DEUTSCH
    if (stricmp(date, "ZULETZT") == 0)
#else
    if (stricmp(date, "LAST") == 0)
#endif
    {
      if (file = fopen(CONFIG_NAME, "r")) {
        fread((char *) CompDate, 1, sizeof(CompDate), file);
        fclose(file);
      }
#ifdef DEUTSCH
      else
        printf("Altes Datum kann nicht gelesen werden.\n"
             "Es wird eine komplette Sicherheitskopie erstellt.\n");
#else
      else
        printf("Error reading old date\n"
               "Doing complete backup instead\n");
#endif
    } else if (!StrToDs(date, (struct DateStamp *) CompDate)) {
#ifdef DEUTSCH
      printf("Falsches Datumsformat !!!\n");
#else
      printf("Wrong date format\n");
#endif
      CloseAll(5);
    }
  }
  OpenAll(Drive);
  if (foundappend) {
    LONG            dnr, pos;

    do {
      do {
        char            buf[5];

        buf[4] = 0;
        TrackDiskBlock->iotd_Req.io_Length = 0;
        DeviceCommand(TrackDiskBlock, (UWORD) TD_MOTOR);
#ifdef DEUTSCH
        printf("Kopie-Diskette #%d in Laufwerk DF%d: einlegen\n", DiskNr, Drive);
#else
        printf("Insert backup disk #%d in drive DF%d:\n", DiskNr, Drive);
#endif
        WaitDisk(TrackDiskBlock);
        ReadTrack(TrackDiskBlock, TrackBuffer, LabelBuffer, 0);
        strncpy(buf, TrackBuffer, 4);
        if (buf[0] != 'B') {
#ifdef DEUTSCH
          printf("Keine Kopie-Diskette !!!\n");
#else
          printf("Not a backup disk\n");
#endif
          UserRequest();
          dnr = 0;
        } else {
          dnr = atoi(buf + 1);
          if (dnr != DiskNr)
            printf("Falsche Diskette #%d !!!\n", dnr);
        }
      } while (dnr != DiskNr);
      pos = ((LONG *) TrackBuffer)[1];
      if (pos == -1) {
#ifdef DEUTSCH
        printf("Diskette voll\n");
#else
        printf("Disk full\n");
#endif
        DiskNr++;
      }
    } while (pos == -1);
    TrackBuffer[4] = TrackBuffer[5] = TrackBuffer[6] = TrackBuffer[7] = 0xff;
    FormatTrack(TrackDiskBlock, 0, TrackBuffer);
    TrackNr = pos / TRACK_SIZE;
    ByteNr = pos % TRACK_SIZE - 1;
    ReadTrack(TrackDiskBlock, TrackBuffer, LabelBuffer, TrackNr + 1);
  } else {
    NewDisk();
    TrackBuffer[8] = 0;
    ByteNr = 8;
  }
  TrackNr--;
  if (foundliste) {
    if (ListFile = fopen(listname, "w")) {
      unsigned char   clock[8];
      char            time[9];
#ifndef DEUTSCH
      char            date[20];
#endif

      fprintf(ListFile, "EasyBackup V1.0 ©1990 Oliver Enseling\n");
      getclk(clock);
      stptime(time, 2, clock);
#ifdef DEUTSCH
      fprintf(ListFile, "Verzeichnis der Sicherheitskopie  %d.%d.%d %s\n",
              (int) clock[3], (int) clock[2], 1980 + clock[1], time);
#else
      stpdate(date, 5, clock);
      fprintf(ListFile, "Backup directory  %s %s\n", date, time);
#endif
      fprintf(ListFile, "========================================="
              "========================================\n");
#ifdef DEUTSCH
      fprintf(ListFile, "Diskette Spur Kopf Dateiname                    "
              "                          Länge\n");
#else
      fprintf(ListFile, "   Disk Track Head Filename                     "
              "                         Length\n");
#endif
      fprintf(ListFile, "-----------------------------------------"
              "----------------------------------------\n");
    }
#ifdef DEUTSCH
    else
      printf("Listfile-Error: %d\n", _OSERR);
#else
    else
      poserr("ListFile");
#endif
  }
  if (BackupTree("")) {
    DateStamp(CompDate);
    if (file = fopen(CONFIG_NAME, "w")) {
      fwrite((char *) CompDate, 1, sizeof(CompDate), file);
      fclose(file);
    }
#ifdef DEUTSCH
    printf("Sicherheitskopie komplett.\n");
#else
    printf("Backup complete\n");
#endif
  }
#ifdef DEUTSCH
  else
    printf("Sicherheitskopie abgebrochen.\n");
#else
  else
    printf("Backup aborted\n");
#endif
  if (ListFile)
    fclose(ListFile);
  CloseAll(0);
}
