
#include "tar.h"
#include "args.h"
#include "timecon.h"
#include "perms.h"
#include "dir.h"
#include "disc.h"
#include "tapeio.h"
#include "replace.h"


/* replace and create routines */


static void tomodes(CatInfo *InfoBlk, int mode) {
  memset(Block.Block,0,RECORDSIZE);
  sprintf(Block.Header.mode, "%6o",mode);
  sprintf(Block.Header.uid, "%6o", 0);
  sprintf(Block.Header.gid, "%6o", 0);
  sprintf(Block.Header.mtime, "%11lo",
          fs_to_unix_time((unsigned char *)&InfoBlk->DateAndName));
  if (!UnixArchive) {
    sprintf(Block.Header.magic, "%s", ARC_MAGIC);
    sprintf(Block.Header.LoadAddress,"%11lo", InfoBlk->LoadAddress);
    sprintf(Block.Header.ExecAddress,"%11lo", InfoBlk->ExecAddress);
    sprintf(Block.Header.Attr,"%11lo", InfoBlk->Attr);
    sprintf(Block.Header.Date,"%2.2x %8.8lx",(int)InfoBlk->DateAndName[4],
            *(long *)&InfoBlk->DateAndName);
  }
} /* tomodes */


static void PutObject(char *longname, char *ShortName, char *parent) {
  int i, compressed, ExtentNo, bytes;
  long restlength, freespace, extentlength;
  char *cp, *filename;
  long blocks;
  FILE *infile;
  os_gbpbstr blk;
  CatInfo InfoBuf;
  char buf[RECORDSIZE];
  char newparent[NAMSIZ+64];
  os_regset regs;

  if (ConfirmActions && !QuietExecution) {
    fprintf(stderr, "tar: %s: ",longname);
    if (Decision('y') == 'n') {
      return;
    }
  }
  blk.action = 11 /* OSGBPB_ReadDirEntriesCatInfo */;
  blk.file_handle = (int) parent;
  blk.data_addr = (void *)&InfoBuf;
  blk.seq_point = 0;
  blk.buf_len = sizeof(struct catinfo);
  blk.wild_fld = ShortName;
  do {
    blk.number = 32767 /* 77 - blk.seq_point */;
    chkos(os_gbpb(&blk));
  } while (blk.number == 0 && blk.seq_point > 0);
  if (blk.number != 1) {
    fprintf(stderr, "tar: %s: info not found\n", longname);
    return;
  }

  switch ((ObjectType)InfoBuf.Type) {
    case t_DirFound:
      if (MultipleVolumes) {
        freespace = FreeOnTape()-2*RECORDSIZE;
        if (freespace < 0)
          NewDisc();
      }
      for (i = 0, cp = buf; *cp = longname[i++], *cp++;)
        ;
      *--cp = FS_DIRSEP;
      *++cp = 0;
      if ((cp - buf) >= NAMSIZ) {
        fprintf(stderr, "tar: %s: file name too long\n",longname);
        return;
      }
      InfoBuf.Length = 0;
      tomodes(&InfoBuf,unix_dir_perms(InfoBuf.Attr));
      sprintf(Block.Header.size, "%11lo", (long)0);
      strcpy(Block.Header.name,buf);
      if (!UnixArchive || name_to_unix(Block.Header.name)) {
        if (!UnixArchive)
          Block.Header.archieflag = ARC_FLAG;    
        Block.Header.linkflag = LF_DIR;
        sprintf(Block.Header.chksum, "%6o", checksum(&Block));
        writetape((char *)&Block);
      }
      sprintf(newparent, "%s%c%s", parent, FS_DIRSEP, ShortName);
#if WITH_PWD
      ChangeCurrentDir(ShortName);
#endif
      blk.file_handle = (int) newparent;
      blk.seq_point = 0;
      blk.wild_fld = "*";
      do {
        blk.number = 1;
        chkos(os_gbpb(&blk));
        if (blk.number == 1 && !UserInterrupt) {
          strcpy(cp, (char *)&InfoBuf.DateAndName[5]);
          PutObject(buf, cp, newparent);
        }
      } while (blk.seq_point > 0 && !UserInterrupt);
#if WITH_PWD
      ChangeCurrentDir(parent);
#endif
      break;

    case t_FileFound:
    case t_ImageFileFound:
      compression = 0;
      if (CompressFiles && InfoBuf.Length > RECORDSIZE) {
        if (ExecuteCommand(CompressTemplate,ShortName,ScrapNameZ,0)) {
          regs.r[0] = 5;
          regs.r[1] = (int)ScrapNameZ;
          if (os_swix(OS_File,&regs) == NULL &&
              (ObjectType)regs.r[0] == t_FileFound)
            compression = (int)((long)100-
                                (long)regs.r[4]*(long)100/InfoBuf.Length);
        }
      }
      if (compression > 0) {
        InfoBuf.Length = (long)regs.r[4];
        filename = ScrapNameZ;
        compressed = 1;
      } else {
        filename = ShortName;
        compressed = 0;
      }
      if (strlen(longname) >= NAMSIZ) {
        fprintf(stderr, "tar: %s: file name too long\n",longname);
        return;
      }
      if ((infile = fopen(filename, "rb")) == NULL) {
        fprintf(stderr, "tar: %s: cannot open file\n", filename);
        return;
      }
      tomodes(&InfoBuf,fs_to_unix_perms(InfoBuf.Attr, InfoBuf.LoadAddress));
      sprintf(Block.Header.compression,"%11o ",compression);
      strcpy(Block.Header.name, longname);
      if (UnixArchive) {
        if (!name_to_unix(Block.Header.name)) {
          return;
        }
        if (AddFileTypeExtension && (InfoBuf.LoadAddress & 0xFFF00000)) {
          sprintf(Block.Header.name + strlen(Block.Header.name), ".%s",
            FileTypeName((int)(InfoBuf.LoadAddress >> 8) & 0xFFF));
        }
        if (CommaFileTypes && (InfoBuf.LoadAddress & 0xFFF00000) &&
            ((InfoBuf.LoadAddress >> 8) & 0xFFF) != ft_Text) {
          sprintf(Block.Header.name + strlen(Block.Header.name), ",%.3lX",
            (InfoBuf.LoadAddress >> 8) & 0xFFF);
        }
      } else {
        Block.Header.archieflag = ARC_FLAG;
      }
      if (compressed)
        Block.Header.compressed =  CF_COMPRESSED;
      restlength = InfoBuf.Length;
      ExtentNo = 0;
      do {
        extentlength = restlength;
        if (MultipleVolumes) {
          freespace = FreeOnTape()-2*RECORDSIZE;
          if (extentlength > freespace) {
            if (freespace < extentlength/10 && freespace < MAXUNUSED) {
              NewDisc();
              freespace = FreeOnTape()-2*RECORDSIZE;
            }
            extentlength = freespace;
            if (extentlength > restlength)
              extentlength = restlength;
            if (ExtentNo > 0 || extentlength < restlength)
              ExtentNo++;
          }
        }
        sprintf(Block.Header.size, "%11lo", extentlength);
        if (ExtentNo > 0) {
          Block.Header.linkflag = (extentlength == restlength)
                                 ? LF_LASTEXTENT : LF_EXTENT;
          sprintf(Block.Header.ExtentNo,"%6o",ExtentNo);
        } else {
          Block.Header.linkflag = LF_NORMAL;
        }
        blocks = (extentlength + (RECORDSIZE-1)) / RECORDSIZE;
        if (Verbose) {
          if (tapedevice == tapedevice_DISC || MultipleVolumes)
            fprintf(stderr,"%3ldK ",FreeOnTape()/(long)1024);
          fprintf(stderr,"a ");
          if (CompressFiles) {
            if (compressed)
              fprintf(stderr,"%2d%% ",compression);
            else
              fprintf(stderr,"    ");
          }
          fprintf(stderr,"%s, %ld bytes, ",longname,extentlength);
          PrintBlocks(stderr,blocks);
          if (ExtentNo > 0)
            fprintf(stderr," (extent #%d)",ExtentNo);
          fputc('\n',stderr);
        }
        sprintf(Block.Header.chksum, "%6o", checksum(&Block));
        writetape((char *)&Block);
        while (blocks > 0 && (bytes = fread(&buf,1,RECORDSIZE,infile)) > 0) {
          if (bytes < RECORDSIZE)
            memset(buf+bytes, 0, RECORDSIZE-bytes);
          writetape(buf);
          blocks--;
        }
        restlength -= extentlength;
      } while (restlength > 0 && !UserInterrupt);
      fclose(infile);
      if (blocks != 0)
        fprintf(stderr, "tar: %s: file not complete\n",longname);
      while (--blocks >=  0)
        putempty();
      if (CompressFiles)
        compress_cleanup();
      break;

    default:
      fprintf(stderr, "tar: %s is not a file. Not dumped\n",longname);
      break;
  }
} /* PutObject */


void ReplaceFiles(void) {
  char *cp, *cp2, *OrgLeafName, *LeafBrowse;
  os_gbpbstr DirBlk;
  CatInfo InfoBuf;
#if WITH_PWD
  char parent[256], *tmp;
  char wdir[MAXPATHLEN], tempdir[MAXPATHLEN];
  char RealName[MAXPATHLEN], longname[MAXPATHLEN];
#endif

  if (!CreateArchive) {
    while (!UserInterrupt) {
      getdir(&InfoBuf);
      if (endtape())
        break;
      switch (Block.Header.linkflag) {
        case LF_LASTEXTENT:
        case LF_EXTENT:
        case LF_NORMAL:
        case LF_OLDNORMAL:
        case LF_DIR:
          passtape(InfoBuf.Length);
          break;
        default:
          break;
      }
    }
    if (UserInterrupt)
      return;
    backtape();
  }

#if WITH_PWD
  GetCurrentDir(wdir);
#endif
  while (!UserInterrupt && (cp2 = GetArg(1), cp2)) {
#if WITH_PWD
    if (!strcmp(cp2, "-C") && (cp2 = GetArg(1), cp2)) {
#if WITH_PWD
      ChangeCurrentDir(cp2);
      (void)GetCurrentDir(wdir);
#else
      
#endif
      continue;
    }
#endif
#if WITH_PWD
    strcpy(parent, wdir);
#endif
    strcpy(longname,cp2);
    OrgLeafName = cp2;
    cp2 = longname;
    LeafBrowse = OrgLeafName;
    for (cp = longname; *cp; cp++, LeafBrowse++)
      if (*cp == FS_DIRSEP) {
        cp2 = cp;
        OrgLeafName = LeafBrowse;
      }
    if (cp2 != longname) {
      *cp2 = '\0';
#if WITH_PWD
      ChangeCurrentDir(longname);
      if ((tmp = GetCurrentDir(tempdir)) == NULL) {
        strcpy(parent, longname);
      } else {
        strcpy(parent, tmp);
      }
#endif
      *cp2 = FS_DIRSEP;
      cp2++;
      OrgLeafName++;
    }
    DirBlk.action = 9;
    DirBlk.file_handle = (int)parent;
    DirBlk.data_addr = (void *)&RealName;
    DirBlk.seq_point = 0;
    DirBlk.buf_len = sizeof(RealName);
    DirBlk.wild_fld = OrgLeafName;
    do {
      DirBlk.number = 1;
      chkos(os_gbpb(&DirBlk));
      if (DirBlk.number == 1 && !UserInterrupt) {
        if (cp2 != longname) {
          *cp2 = '\0';
          strcat(longname,RealName);
        } else {
          strcpy(longname,RealName);
        }
        PutObject(longname, RealName, parent);
      }
    } while (DirBlk.seq_point > 0 && !UserInterrupt);
#if WITH_PWD
    ChangeCurrentDir(wdir);
#endif
  }
  putempty();
  putempty();
  flushtape();
} /* ReplaceFiles */
