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

extern void     UserRequest(), EndRestore();

#define EndBackup EndRestore

#define DEUTSCH

#include "support.c"
#include "restore.i"

LONG            TrackNr, ByteNr;
int             Drive;
char           *Dest, *Pattern;
char            DirPath[FMSIZE] = "";
BOOL            FoundInter = FALSE, FoundArchiviere = FALSE;

/* SetDate: Datum einer Datei setzen */

void
SetDate(fn, ds)
    char           *fn;
    struct DateStamp *ds;
{
  struct StandardPacket *sp;
  BPTR            fl, dl;
  char           *buf;
  char            name[80];

  if (fl = Lock(fn, SHARED_LOCK)) {
    if (!(dl = ParentDir(fl))) {
      UnLock(fl);
      return;
    }
  } else
    return;
  UnLock(fl);
  stcgfn(name, fn);
  if (buf = AllocMem(strlen(name) + 2, MEMF_CLEAR | MEMF_PUBLIC)) {
    strcpy(buf + 1, name);
    buf[0] = strlen(name);
    if (sp = AllocMem(sizeof(struct StandardPacket), MEMF_CLEAR | MEMF_PUBLIC)) {
      struct Process *p = (struct Process *) FindTask(NULL);
      sp->sp_Msg.mn_Node.ln_Name = (char *) &sp->sp_Pkt;
      sp->sp_Pkt.dp_Link = &sp->sp_Msg;
      sp->sp_Pkt.dp_Port = &p->pr_MsgPort;
      sp->sp_Pkt.dp_Type = 34;
      sp->sp_Pkt.dp_Arg2 = (LONG) dl;
      sp->sp_Pkt.dp_Arg3 = (LONG) buf >> 2;
      sp->sp_Pkt.dp_Arg4 = (LONG) ds;
      PutMsg((struct MsgPort *) ((struct FileLock *) ((LONG) dl << 2))->fl_Task,
             (struct Message *) sp);
      WaitPort(&p->pr_MsgPort);
      GetMsg(&p->pr_MsgPort);
      FreeMem(sp, sizeof(*sp));
    }
    FreeMem(buf, buf[0] + 2);
  }
  UnLock(dl);
}

/* NewDisk: neue Diskette anfordern */

LONG            DiskNr = 0, EndNr = 0x7fffffff;
void
NewDisk()
{
  char            buf[5];
  LONG            dnr;

  TrackDiskBlock->iotd_Req.io_Length = 0;
  DeviceCommand((struct IORequest *)TrackDiskBlock, (UWORD) TD_MOTOR);
  DiskNr++;
  do {
#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
    Beep(880, 25, 64);
    WaitDisk(TrackDiskBlock);
    ReadTrack(TrackDiskBlock, TrackBuffer, LabelBuffer, 0);
    setmem(buf, 5, 0);
    strncpy(buf, TrackBuffer, 4);
    if (buf[0] != 'B') {
      dnr = 0;
#ifdef DEUTSCH
      printf("Keine Kopie-Diskette !!!");
#else
      printf("Not a backup disk\n");
#endif
      UserRequest();
    } else {
      dnr = atoi(buf + 1);
#ifdef DEUTSCH
      if (dnr != DiskNr)
        printf("Falsche Diskette #%d !!!\n", dnr);
#else
      if (dnr != DiskNr)
        printf("Wrong disk #%d !!!\n", dnr);
#endif
    }
  } while (dnr != DiskNr);
#ifdef DEUTSCH
  printf("Diskette OK\n");
#else
  printf("Disk OK\n");
#endif
  TrackNr = 0;
  ByteNr = 8 + strlen(&TrackBuffer[8]) + 1;
  strcpy(DirPath, &TrackBuffer[8]);
  PrintDisk(DiskNr);
  PrintTrack(0);
}

/* RawGetByte: ein Byte aus dem Backup lesen */

BYTE
RawGetByte()
{
  BYTE            b;

  ByteNr++;
  if (ByteNr >= TRACK_SIZE) {
    TrackNr++;
    if (TrackNr >= MAX_TRACKS) {
#ifdef DEUTSCH
      printf("Diskette fertig reinstalliert\n");
#else
      printf("Disk completely reinstalled\n");
#endif
      NewDisk();
    } else {
      ByteNr = 0;
      ReadTrack(TrackDiskBlock, TrackBuffer, LabelBuffer, TrackNr);
      PrintTrack(TrackNr);
    }
  }
  b = TrackBuffer[ByteNr];
  return (b);
}

/* Byte unter Berücksichtigung von Dateiendemarkierungen lesen */

BOOL            DateiEnde = FALSE;

char
DecodeByte()
{
  char            b;
  static unsigned char buffer;
  static BOOL     buffer_on = FALSE;

  if (buffer_on) {
    b = buffer;
    buffer_on = FALSE;
  } else
    b = RawGetByte();
  if (b == DATEIENDEMARKE) {
    buffer = RawGetByte();
    if (buffer == DATEIENDEMARKE) {
      buffer_on = FALSE;
      DateiEnde = FALSE;
    } else {
      buffer_on = TRUE;
      DateiEnde = TRUE;
    }
    return (DATEIENDEMARKE);
  } else {
    DateiEnde = FALSE;
    return (b);
  }
}

/* RawReadString: eine String mit Endung 0 aus dem Backup lesen */

void
RawReadString(str)
    char           *str;
{
  char            c;

  do {
    c = DecodeByte();
    *str++ = c;
  } while (c != 0);
}

/* RawRead: Anzahl Bytes aus dem Backup lesen */

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

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

/* EndRestore: Drive-Motor ausschalten + KontrollMsg ausgeben */

void
EndRestore()
{
  TrackDiskBlock->iotd_Req.io_Length = 0;
  DeviceCommand((struct IORequest *)TrackDiskBlock, (UWORD) TD_MOTOR);
}

/*
 * MakePath: testen, ob alle dirs zu einem kompletten Dateinamen
 * vorhanden sind, ggf. neue dirs erstellen
 */

void
MakePath(path)
    char           *path;
{
  char            buffer[512];
  BPTR            lock;
  char           *pos = path;

  while (pos = stpchr(pos + 1, '/')) {
    strncpy(buffer, path, (int) pos - (int) path);
    buffer[(int) pos - (int) path] = 0;
    if (!(lock = Lock(buffer, ACCESS_READ)))
      if (!(lock = CreateDir(buffer))) {
#ifdef DEUTSCH
        printf("Verzeichnis %s kann nicht erstellt werden.\n", buffer);
#else
        printf("Unable to create directory %s\n", buffer);
#endif
        UserRequest();
        return;
      }
    UnLock(lock);
  }
}

/* RestoreFileData: Inhalt einer Datei lesen und in File kopieren */

void
RestoreFileData(file)
    int             file;
{
  BYTE            b;
  LONG            i = 0;

  for (b = DecodeByte(); !DateiEnde; b = DecodeByte()) {
    FileBuffer[i] = b;
    i++;
    if (i >= BUFFER_SIZE) {
      write(file, FileBuffer, BUFFER_SIZE);
      i = 0;
    }
  }
  write(file, FileBuffer, i);
}

/* Ende einer Datei suchen */

void
GotoDateiEnde()
{
  BYTE            b;

  for (b = DecodeByte(); !DateiEnde; b = DecodeByte());
}

/* RestoreDir: Verzeichnisse rekursiv reinstallieren */

BOOL
RestoreDir(doinstall)
    BOOL            doinstall;
{
  LONG            err, protection;
  char            d[FMSIZE], filename[FMSIZE], path[FMSIZE], fullpath[FMSIZE],
                  comment[116];
  struct DateStamp date;
  int             file;

  strcpy(d, DirPath);
  FOREVER
  {
    err = 0;
    if (DiskNr > EndNr)
      CloseAll(0);
    switch (DecodeByte()) {
    case ID_DIR:
      RawReadString(filename);
      ConcatPath(DirPath, d, filename);
      if (doinstall) {
#ifdef DEUTSCH
        printf("Verzeichnis \033[32m%s\033[0m ", DirPath);
#else
        printf("Directory   \033[32m%s\033[0m ", DirPath);
#endif
        if (FoundInter) {
          if (FileAbfrage())
            RestoreDir(TRUE);
          else
            RestoreDir(FALSE);
        } else
          RestoreDir(TRUE);
      } else
        RestoreDir(FALSE);
      break;
    case ID_ENDDIR:
      return (TRUE);
    case ID_FILE:
      RawReadString(filename);
      RawRead((BYTE *) & protection, sizeof(LONG));
      RawRead((BYTE *) & date, sizeof(struct DateStamp));
      RawReadString(comment);
      ConcatPath(path, d, filename);
      if (doinstall && (!Pattern || astcsma(path, Pattern))) {
        BOOL            restorefile = TRUE;
        ConcatPath(fullpath, Dest, path);
#ifdef DEUTSCH
        printf("Reinstallation von \033[33m%s\033[31m ... ", fullpath);
#else
        printf("reinstalling       \033[33m%s\033[31m ... ", fullpath);
#endif
        if (FoundInter)
          restorefile = FileAbfrage();
        if (restorefile) {
          MakePath(fullpath);
          if ((file = open(fullpath, O_WRONLY | O_CREAT)) != -1) {
            RestoreFileData(file);
            close(file);
            if (FoundArchiviere)
              protection |= FIBF_ARCHIVE;
            SetProtection(fullpath, protection);
            if (!(err = IoErr()))
              {
              SetComment(fullpath, comment);
              if (!(err = IoErr()))
                {
                SetDate(fullpath, &date);
                }
              }
          }
          else err = _OSERR;
          if (err) {
#ifdef DEUTSCH
            printf("\nFehler bei der Reinstallation.\nDOS-Fehlernummer:%4d\n", err);
#else
            printf("\nError while reinstalling\nDOS-Error:%4d\n", err);
#endif
            UserRequest();
          } else
            printf("OK\n");
        } else
          GotoDateiEnde();
      } else
        GotoDateiEnde();
      break;
    default:
#ifdef DEUTSCH
      printf("Backupstruktur fehlerhaft !!!\n"
             "Suche nächsten Eintrag\n");
#else
      printf("Error in backup structure\n"
             "Searching next entry\n");
#endif
      GotoDateiEnde();
    }
  }
  return (TRUE);
}

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

void
main(argc, argv)
    int             argc;
    char          **argv;
{
  BOOL            foundfrom = FALSE, foundto = FALSE, foundpattern = FALSE, foundstart = TRUE,
                  foundend = FALSE;
  char           *source;
  int             i;

  printf("EasyBackup V1.0, Restore  ©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], "MUSTER") == 0)
#else
        if (stricmp(argv[i], "PATTERN") == 0)
#endif
        {
          foundpattern = TRUE;
          i++;
          Pattern = argv[i];
        } else {
#ifdef DEUTSCH
          if (stricmp(argv[i], "START") == 0)
#else
          if (stricmp(argv[i], "START") == 0)
#endif
          {
            foundstart = TRUE;
            i++;
            stcd_l(argv[i], &DiskNr);
            DiskNr--;
          } else {
#ifdef DEUTSCH
            if (stricmp(argv[i], "ENDE") == 0)
#else
            if (stricmp(argv[i], "END") == 0)
#endif
            {
              foundend = TRUE;
              i++;
              stcd_l(argv[i], &EndNr);
            } 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], "ARCHIVIERE") == 0)
#else
                if (stricmp(argv[i], "ARCHIVATE") == 0)
#endif
                {
                  FoundArchiviere = 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");
#else
                    else
                      printf("Argument \033[32m%sy\033[31m ignored\n");
#endif
                  }
                }
              }
            }
          }
        }
      }
    }
  }
  if (!(foundfrom && foundto)) {
#ifdef DEUTSCH
    printf("Syntax: Restore [VON] <laufwerk> [NACH] <ver> [MUSTER <muster>]\n"
           "                [START <startnr>] [ENDE <endenr>] [INTERAKTIV]\n"
           "                [ARCHIVIERE]\n"
           "        laufwerk - Das Laufwerk, mit dem gearbeitet werden soll\n"
           "        ver ------ Das Zielverzeichnis\n"
           "        muster --- Ein AmigaDOS-übliches Muster, nur die Dateien,\n"
      "                   die hierauf passen werden reinstalliert\n"
           "        startnr -- Nummer der Diskette, bei der die Reinstallation\n"
           "                   starten soll\n"
           "        endenr --- Nummer der Diskette, bei der die Reinstallation\n"
           "                   enden soll\n"
           "        \n"
           "        Die Schlüsselwörter VON und NACH können\n"
           "        weggelassen werden.\n");
#else
    printf("Usage: Restore [FROM] <drive> [TO] <dir> [PATTERN <pattern>]\n"
           "               [START <startno>] [END <endno>] [QUERY] [ARCHIVATE]\n"
           "       drive ---- Disk drive for backup disks\n"
           "       dir ------ Target directory for restoration\n"
           "       pattern -- standard pattern for wildcard pattern matching like\n"
           "                  AmigaDOS commands to select files to be restored\n"
           "       startno -- first disk to be restored\n"
           "       endno ---- last disk to be restored\n"
           "       keywords FROM and TO can be left out\n");
#endif
    CloseAll(1);
  }
  Drive = -1;
  if (stricmp("DF0:", source) == 0)
    Drive = 0;
  if (stricmp("DF1:", source) == 0)
    Drive = 1;
  if (stricmp("DF2:", source) == 0)
    Drive = 2;
  if (stricmp("DF3:", source) == 0)
    Drive = 3;
  if (Drive < 0) {
#ifdef DEUTSCH
    printf("Falsches Diskettenlaufwerk %s !!!\n", source);
#else
    printf("Wrong disk drive %s\n", source);
#endif
    CloseAll(5);
  }
  OpenAll(Drive);
  NewDisk();
  ByteNr--;
  if (DiskNr > 1)
    GotoDateiEnde();
#ifdef DEUTSCH
  if (RestoreDir(TRUE))
    printf("Reinstallation komplett\n");
  else
    printf("Reinstallation abgebrochen\n");
#else
  if (RestoreDir(TRUE))
    printf("Restoration complete\n");
  else
    printf("Restoration aborted\n");
#endif
  CloseAll(0);
}
