
/* Tape Archive Programme */

#include <signal.h>

#include "tar.h"
#include "args.h"
#include "options.h"
#include "perms.h"
#include "dir.h"
#include "rmt.h"
#include "tapeio.h"
#include "replace.h"
#include "table.h"
#include "extract.h"

static char *version = "[1.2b] (" __DATE__ ")";

int CreateArchive;
int ArchiveFileSpecified;
int IgnoreArchiveErrors;
int DoNotExtractFileDates;
int UseListFile;
int ConvertExclamationMark;
int AppendToArchive;
int SwapExtensionToDir;
int ListArchivesContents;
int Verbose;
int ConfirmActions;
int ExtractFromArchive;
int CompressFiles;
int Reblock;
int PeriodSlashConversion;
int FormatFloppies;
int MultipleVolumes;
int GNUmultipleVolumes;
int NoDiskDestroyConfirmation;
int QuietExecution;
int SwapInWholePath;
int CommaFileTypes;
int UnixArchive;
int VeryVerbose;
int AddFileTypeExtension;
int ConvertCompressExtension;
int UseCanonicalisedPaths;
int MaxLeafLength = ADFS_MAXLEAFLENGTH;

int tarFileType;

int MaxExtLength = 3;
tapedevice_t tapedevice;
int driveno;
char format = 'E';

char *listfile = "!tarlist";
char *ArchiveName;
int ArchiveOpen;
FILE *ArchiveFD;
/*int rmt_fd;*/

int UserInterrupt;

Block_t *TmpBlock;
Block_t Block;

int NumBlocksRead;
int nblock=20;
int recno;
int first;
int DiscNo=1;
int compression;

int isArchie;

#if WITH_PWD
int InitialUnset;

char InitialPreviousDir[MAXPATHLEN];
char InitialCurrentDir[MAXPATHLEN];
#endif

char ScrapName[MAXPATHLEN];
char ScrapNameZ[MAXPATHLEN];

char CompressTemplate[MAXPATHLEN];
char DecompressTemplate[MAXPATHLEN];
char CompressExt[256];

static int ArchiveIsStdIO;
static int GotInitialDirectories = 0;


void Terminate(int n) {
  if (GotInitialDirectories) {
#if WITH_PWD
    if (InitialUnset) {
      os_cli("nodir");
    } else {
      ChangeCurrentDir(InitialPreviousDir);
      ChangeCurrentDir(InitialCurrentDir);
    }
#endif
  }
  switch (tapedevice) {
    case tapedevice_FILE:
      if (ArchiveOpen) {
        fclose(ArchiveFD);
        if (AppendToArchive && !ArchiveIsStdIO) {
          SetFileType(ArchiveName, tarFileType);
        }
      }
      break;
#ifndef __riscos
    case tapedevice_RMT:
      if (ArchiveOpen) {
        rmtclose(rmt_fd);
      }
      break;
#endif
  }
  exit(n);
} /* Terminate */


char Decision(char default_answer) {
  char c;

  c = getchar();
  if (c != '\n')
    while (getchar() != '\n')
      ;
  else
    c = default_answer;
  return (c);
} /* Decision */


int chkos(os_error *Error) {
  char answer;

  if (Error != NULL) {
    fprintf(stderr, "tar: OS error: %s\n", Error->errmess);
    if (!QuietExecution) {
      fprintf(stderr, "tar: continue? ");
      answer = Decision('y');
    }
    if (QuietExecution || answer == 'n' || answer == 'N') {
      Terminate(15);
    }
    return(0);
  }
  return(1);
} /* chkos */


int ExecuteCommand(char *Template, char *Arg1, char *Arg2, int Warn) {
  char *sp, *dp;
  char OSCommand[256];

  dp = OSCommand;
  while (*Template) {
    if (*(Template) != '%')
      *(dp++) = *(Template++);
    else {
      Template++;
      sp = NULL;
      if (*Template == '1')
        sp = Arg1;
      else if (*Template == '2')
        sp = Arg2;
      if (sp == NULL)
        *(dp++) = *(Template - 1);
      else {
        while (*sp)
          *(dp++) = *(sp++);
        Template++;
      }
    }
  }
  *dp = '\0';
  if (system(OSCommand) != 0) {
    if (Warn)
      fprintf(stderr,"Warning: \"%s\" failed.\n",OSCommand);
    return 0;
  }
  return 1;
} /* ExecuteCommand */


void InterruptHandler(int dummy) {
  signal(SIGINT, SIG_IGN);
  UserInterrupt++;
} /* InterruptHandler */


void AbortHandler(int dummy) {
  signal(SIGABRT, SIG_IGN);
  UserInterrupt++;
} /* AbortHandler */


void TerminateHandler(int dummy) {
  signal(SIGTERM, SIG_IGN);
  UserInterrupt++;
} /* TerminateHandler */


void compress_cleanup(void) {
  remove(ScrapName);
  remove(ScrapNameZ);
} /* compress_cleanup */


void PrintBlocks(FILE *fd, long blocks) {
  fprintf(fd,"%ld block",blocks);
  if (blocks != 1)
    fputc('s',fd);
} /* PrintBlocks */


void usage(void) {
  fprintf(stderr,"tar: usage: tar [-]cmd[opt] ARCHIVE [LIST] [BLKS] [-C DIR] file1 file2 ...\n");
  fputs("\
Commands: c          create a new archive\n\
          r          append files to end of archive\n\
          t          list the contents of an archive\n\
          x          extract files from an archive\n\
",stderr);
  fputs("\
Options:  b BLKS     blocking factor BLKS (block size = BLKS x 512 bytes)\n\
          e LENGTH   maximum length of extension to use in UNIX file names\n\
          f ARCHIVE  read/write archive from file ARCHIVE (mandatory)\n\
          i          ignore checksum errors and zero blocks (normally EOF)\n\
          l [LIST]   read list of files from file LIST or !tarlist as default\n\
          m          do not extract date and times of files\n\
          p          convert '!' in RISC OS names to '_' in UNIX names\n\
          s          swap extension in filename (see documentation)\n\
          v          verbosely list what files we process\n\
          w          ask for confirmation\n\
          z          compress files before archiving\n\
          B          re-block as we read\n\
          C DIR      change to directory DIR\n\
          E          '.' in UNIX names will be converted to '/'\n\
          F          format disc in drive 0 (multiple volumes & E-format only)\n\
          G          use GNU tar multiple volume archiving\n\
          M          use multiple volume archiving\n\
          O          do not ask for confimation to format or write raw discs\n\
          L LEN      maximum file/directory name is LEN when extracting (default 10)\n\
          P          use RISC OS 3 path canonicalise instead of own routine\n\
          Q          quit immediately if error occurs, do not prompt for action\n\
          S          swap extensions in whole path\n\
          T          file types appended to names as with RISC-OS NFS\n\
          U          UNIX archive mode (no Archimedes extensions)\n\
          V          very verbose: also display files that are not extracted\n\
          X          add RISC OS file type as extension to file name in archive\n\
          Z          convert UNIX '.Z' to '/Z' when extracting\n\
Please read the documentation file for further information. I will take no\n\
responsibility for damage or loss of data due to the use of this programme.\n",
 stderr);
  fprintf(stderr,
    "RISC OS tar %s by Frank Lancaster, Copyright (C) 1991-1994.\n", version);
  fprintf(stderr,
    "This programme is public domain. You may not charge anything for it.\n");
  Terminate(16);
} /* usage */


void ChkScrapName(void) {
  char *cp;

  if (ScrapName[0] != '\0')
    return;
  if ((cp = getenv(SCRAP_VAR)) == NULL) {
    fprintf(stderr,"tar: system variable %s not set\n",SCRAP_VAR);
    Terminate(17);
  }
  strcpy(ScrapName,cp);
  strcpy(ScrapNameZ,ScrapName);
  strcat(ScrapNameZ,CompressExt);
  compress_cleanup();
} /* GetScrapName */


int main(int argc, char *argv[]) {
  char *cp;

  if (argc < 2) {
    usage();
  }

  argv[argc] = 0;
  argv++;
  for (cp = *argv++; *cp; cp++)
    argv += CheckOption(&cp,*argv);

#if WITH_PWD
  if ((InitialUnset = CurrentDirUnset()) == 0) {
    ChangeCurrentDir("\\");
    (void)GetCurrentDir(InitialPreviousDir);
    ChangeCurrentDir("\\");
    (void)GetCurrentDir(InitialCurrentDir);
  }
#endif

  GotInitialDirectories = 1;

  if ((cp = getenv(TAR_FILETYPE_VAR)) != NULL)
    tarFileType = (int)strtoul(cp,NULL,16);
  else
    tarFileType = 0xC46;

  if ((cp = getenv(COMPEXT_VAR)) != NULL)
    strcpy(CompressExt,cp);
  else
    strcpy(CompressExt,"/Z");
  if ((cp = getenv(COMPRESS_VAR)) != NULL)
    strcpy(CompressTemplate,cp);
  else
    strcpy(CompressTemplate,"compress < %1 > %2");
  if ((cp = getenv(DECOMPRESS_VAR)) != NULL)
    strcpy(DecompressTemplate,cp);
  else
    strcpy(DecompressTemplate,"compress -d < %1 > %2");

  InitArgs(argv);
  if (!AppendToArchive && !ExtractFromArchive && !ListArchivesContents ||
      AppendToArchive && ExtractFromArchive ||
      ListArchivesContents && ExtractFromArchive ||
      AppendToArchive && ListArchivesContents) {
    usage();
  }

  if (CompressFiles) {
    ChkScrapName();
  }

  if (ArchiveName == NULL && tapedevice != tapedevice_DISC) {
    fprintf(stderr,"tar: no archive file specified; use 'f' option\n");
    Terminate(18);
  }

  TmpBlock = (union HeaderBlock *)malloc(nblock*RECORDSIZE);
  if (TmpBlock == NULL) {
    fprintf(stderr, "tar: blocksize %d too big, can't get memory\n",nblock);
    Terminate(19);
  }
  if (AppendToArchive) {
    if (GetArg(0) == NULL) {
      fprintf(stderr,"tar: error: no files specified\n");
      Terminate(20);
    }
    signal(SIGINT, InterruptHandler);
    signal(SIGABRT, AbortHandler);
    signal(SIGTERM, TerminateHandler);
    if (strcmp(ArchiveName, "-") == 0) {
      if (CreateArchive == 0) {
        fprintf(stderr,"tar: cannot append to standard output archives\n");
        Terminate(21);
      }
      ArchiveIsStdIO = 1;
      ArchiveFD = stdout;
      nblock = 1;
    } else {
      open_writetape();
    }
    ReplaceFiles();
    Terminate(UserInterrupt ? 22 : 0);
  }
  if (strcmp(ArchiveName, "-") == 0) {
    ArchiveIsStdIO = 1;
    ArchiveFD = stdin;
    nblock = 1;
  } else {
    open_readtape();
  }
  if (ExtractFromArchive) {
    ExtractFiles();
  } else {
    ListContents();
  }
  Terminate(UserInterrupt ? 23 : 0);
} /* main */
