/*
  Copyright (c) 1990-1999 Info-ZIP.  All rights reserved.

  See the accompanying file LICENSE, version 1999-Oct-05 or later
  (the contents of which are also included in zip.h) for terms of use.
  If, for some reason, both of these files are missing, the Info-ZIP license
  also may be found at:  ftp://ftp.cdrom.com/pub/infozip/license.html
*/
/*
 * routines only used by TANDEM ZIP
 */

#include "zip.h"
#include <tal.h>
#include "$system.zsysdefs.zsysc" nolist
#include <cextdecs> nolist
#include "tannsk.h"

/******************************/
/*  Function version_local()  */
/******************************/

void version_local()
{
    static ZCONST char CompiledWith[] = "Compiled with %s%s for %s%s%s%s.\n\n";
#if 0
    char buf[40];
#endif

    printf(CompiledWith,

#ifdef __GNUC__
      "gcc ", __VERSION__,
#else
#  if 0
      "cc ", (sprintf(buf, " version %d", _RELEASE), buf),
#  else
      "unknown compiler", "",
#  endif
#endif

      "Tandem/NSK", "",

#ifdef __DATE__
      " on ", __DATE__
#else
      "", ""
#endif
      );

} /* end function version_local() */

  int Bflag = 0;            /* Special formatting options for Tandem        */
                            /* Bit 0 = Add delimiter (0 = Yes, 1 = No)      */
                            /* Bit 1 = Delimiter Type (0 = CR/LF, 1 = LF)   */
                            /* Bit 2 = Space Fill records (0 = No, 1 = Yes) */
                            /* Bit 3 = Trim trailing space(0 = No, 1 = Yes) */
                            /* Thus, default is to add CR/LF, no padding    */
                            /* Bit 8 = Use 'safe' large read size (Expand)  */
  char nsk_delim[2] = {'\r', '\n'}; /* CR/LF */
  int nsk_delim_len = 2;
  int nsk_space_fill = 0;   /* 0 = No, 1 = Yes          */
  int nsk_trim_space = 0;   /* 0 = No, 1 = Yes          */
  unsigned short nsk_large_read = MAX_LARGE_READ;

  /* Following holds details of file currently used by zopen & zread */
  struct stat znsk_stat;
  nsk_stat_ov *znsk_ov = (nsk_stat_ov *)&znsk_stat.st_reserved[0];
  nsk_file_attrs *znsk_attr = (nsk_file_attrs *)
                                ((char *) (&znsk_stat.st_reserved[0]) + 4);

  /* Following items used by zread to avoid overwriting window */
  char zreadbuf[MAX_LARGE_READ];       /* Buffer as large as biggest read */
  char *zreadptr = (char *) zreadbuf;  /* pointer to start of buffer      */
  char *zread_ovptr = NULL;            /* pointer to left overs           */
  long zread_ovlen = 0;                /* size of remaining left overs    */

  int zopen (filename, opt)
    const char *filename;
    int opt;
  {
    /* Currently ignore opt.  Choose method of I/O based on NSK file type */
    short err, len, fnum, access, exclus, bufferlen, options;
    long recnum;
    char fname[FILENAME_MAX + 1];
    short extension;
    char ext[EXTENSION_MAX + 1];

    /* Remove any (pseudo) file extension */
    extension = parsename (filename,fname,ext);
    len = strlen(fname);

    fnum = 0;
    access = NSK_RDONLY;
    exclus = NSK_SHARED;

    err = stat(fname, &znsk_stat); /* Setup global struct, used by zread */

    if (znsk_attr->filetype == NSK_UNSTRUCTURED)
      if (znsk_attr->filecode == NSK_EDITFILECODE) {
        /* Edit File */
        fnum = -1; /* Ask OPENEDIT_ to open the file for us */
        err = OPENEDIT_ ((char *)fname, len, &fnum, access, exclus);
        if (!err) {
          recnum = -1; /* Position to first line */
          err = POSITIONEDIT (fnum, recnum);
        }
      }
      else {
        /* Unstructured File */
        options = NSK_UNSTRUCTUREDACCESS;
        err = FILE_OPEN_((char *)fname, len, &fnum, access, exclus,
                         ,,options,,,);
        if (!err)
          /* Ask for large transfer mode */
          err = (SETMODE (fnum, SET_LARGE_TRANSFERS, 1) != CCE);
      }
    else {
      /* Structured File */
      bufferlen = 4096;  /* request SBB */
      err = FILE_OPEN_((char *)fname, len, &fnum, access, exclus,
                       ,,,, bufferlen ,);
      if (err == 4)
        err = 0;  /* Allow saving of files that have missing altkeys */
    }

    return (err == 0 ? (int)fnum : -1);
  }

  unsigned zread (fnum, buf, len)
    int fnum;
    char *buf;
    unsigned len;
  {
    short err, trail;
    long total, movelen;
    unsigned short countread;
    unsigned readlen;  /* typed to match incoming arg */
    char *bufptr, *readptr;

    total = err = 0;
    bufptr = buf;

    /* We use a separate buffer to read in data as it can be larger than
       WSIZE, and hence would overwrite memory */

    /* We always attempt to give the user the exact requested size
       Hence we make use of an overfow buffer for previously truncated data */

    /* see if we have some left over characters from last time */
    if (zread_ovlen) {
       movelen = _min(len,zread_ovlen);
       memcpy(bufptr, zread_ovptr, movelen);
       bufptr += movelen;
       total += movelen;
       zread_ovptr += movelen;
       zread_ovlen -= movelen;
    }

    while (!err && (total < len)) {
      readptr = zreadptr;

      if (znsk_attr->filetype == NSK_UNSTRUCTURED)
        if (znsk_attr->filecode == NSK_EDITFILECODE){
          /* Edit File */
          trail = 1;
          readlen = MAX_EDIT_READ; /* guarantee it fits in buffer */

          /* get line and preserve any trailing space characters */
          err = READEDIT (fnum,, zreadptr, (short) readlen,
                            (short *) &countread,,, trail);
          /* if countread is ever negative then we will skip a line */

          if (!err) {
            readptr = zreadptr + countread;
            /* Note it is possible for Edit files to hold trailing space */
            if (nsk_trim_space)
              while (*(readptr-1) == ' ') {
                readptr--;
                countread--;
              }

            if (nsk_delim_len) {
              memcpy(readptr, nsk_delim, nsk_delim_len);
              readptr += nsk_delim_len;
              countread += nsk_delim_len;
            }
          }
        }
        else {
          /* Unstructured File */

          /* Using large transfer mode so we have to use 2K multiples
             Use largest size possible and put remains in overflow    */

          readlen = nsk_large_read; /* use largest read, overflow into buffer*/

          err = (READX(fnum, zreadptr, readlen, (short *)&countread) != CCE);
          if (err && (errno == EINVAL)) {
            /* Read too big so scale back to smaller value */
            readlen = nsk_large_read = MAX_LARGE_READ_EXPAND;
            err = (READX(fnum, zreadptr, readlen, (short *)&countread) != CCE);
          }
          if (!err)
            readptr = zreadptr + countread;
        }
      else {
        /* Structured File */
        readlen = znsk_attr->reclen;

        err = (READX(fnum, zreadptr, readlen, (short *)&countread)!= CCE);

        if (!err) {
          readptr = zreadptr + countread;
          if (nsk_space_fill)
            while (countread < readlen) {
              *readptr++ = ' ';
              countread++;
            }
          else
            if (nsk_trim_space)
              while (*(readptr-1) == ' ') {
                readptr--;
                countread--;
              }

          if (nsk_delim_len) {
            memcpy(readptr, nsk_delim, nsk_delim_len);
            readptr += nsk_delim_len;
            countread += nsk_delim_len;
          }
        }
      }
      if (!err) {
         movelen = _min((len-total), countread);
         memcpy(bufptr, zreadptr, movelen);
         bufptr += movelen;
         total += movelen;
         if (movelen < countread) { /* still stuff in Read buffer */
           zread_ovptr = zreadptr + movelen;   /* pointer to whats left */
           zread_ovlen = countread - movelen;  /* how much is left      */
         }
      }
    }

    return ((unsigned)total);
  }

  int zclose (fnum)
    int fnum;
  {
    short err;

    if ((znsk_attr->filetype == NSK_UNSTRUCTURED)
      && (znsk_attr->filecode == NSK_EDITFILECODE))
      err = CLOSEEDIT_(fnum);
    else
      err = FILE_CLOSE_(fnum);

    return (err != 0);
  }

void nskformatopt(p)
char **p;
{
char *q;

  /* set up formatting options for ZIP */

  q = *p; /* make a note of where we are */

  Bflag = 0; /* default option */

  Bflag = strtoul((*p + 1), p, 10);

  /* need to go back one character if we've got a result */
  if (q != *p)
    (*p)--;

  if (Bflag & NSK_SPACE_FILL)
    nsk_space_fill = 1;
  else
    nsk_space_fill = 0;

  if (Bflag & NSK_TRIM_TRAILING_SPACE)
    nsk_trim_space = 1;
  else
    nsk_trim_space = 0;

  if (Bflag & NSK_NO_DELIMITER)
    nsk_delim_len = 0;
  else {
    if (Bflag & NSK_USE_FF_DELIMITER) {
      nsk_delim[0] = '\n';
      nsk_delim_len = 1;
    }
    else {   /* CR/LF */
      nsk_delim[0] = '\r';
      nsk_delim[1] = '\n';
      nsk_delim_len = 2;
    }
  }

  if (Bflag & NSK_LARGE_READ_EXPAND)
    nsk_large_read = MAX_LARGE_READ_EXPAND;
  else
    nsk_large_read = MAX_LARGE_READ;

}


  int deletedir(d)
    char *d;                /* directory to delete */
  /* Delete the directory *d if it is empty, do nothing otherwise.
     Return the result of rmdir(), delete(), or system().
     For VMS, d must be in format [x.y]z.dir;1  (not [x.y.z]).
   */
  {
      return rmdir(d);
  }

  local char *readd(d)
    DIR *d;                 /* directory stream to read from */
  /* Return a pointer to the next name in the directory stream d, or NULL if
     no more entries or an error occurs. */
  {
    struct dirent *e;

    e = readdir(d);
    return e == NULL ? (char *) NULL : e->d_name;
  }

  int procname(n, caseflag)
    char *n;                /* name to process */
    int caseflag;           /* true to force case-sensitive match */
  /* Process a name or sh expression to operate on (or exclude).  Return
     an error code in the ZE_ class. */
  {
    char *a;              /* path and name for recursion */
    DIR *d;               /* directory stream from opendir() */
    char *e;              /* pointer to name from readd() */
    int m;                /* matched flag */
    char *p;              /* path for recursion */
    struct stat s;        /* result of stat() */
    struct zlist far *z;  /* steps through zfiles list */

    if (strcmp(n, "-") == 0)   /* if compressing stdin */
      return newname(n, 0, caseflag);
    else if (stat(n, &s))
    {
      /* Not a file or directory--search for shell expression in zip file */
      p = ex2in(n, 0, (int *)NULL);       /* shouldn't affect matching chars */
      m = 1;
      for (z = zfiles; z != NULL; z = z->nxt) {
        if (MATCH(p, z->zname, caseflag))
        {
          z->mark = pcount ? filter(z->zname, caseflag) : 1;
          if (verbose)
              fprintf(mesg, "zip diagnostic: %scluding %s\n",
                 z->mark ? "in" : "ex", z->name);
          m = 0;
        }
      }
      free((zvoid *)p);
      return m ? ZE_MISS : ZE_OK;
    }

    /* Live name--use if file, recurse if directory */
    if ((s.st_mode & S_IFDIR) == 0)
    {
      /* add or remove name of file */
      if ((m = newname(n, 0, caseflag)) != ZE_OK)
        return m;
    } else {
      if ((p = malloc(strlen(n)+4)) == NULL)
        return ZE_MEM;

      strcpy(p, n);

      /* No concept of directories on Tandem - so do not store them ...*/
      /* code removed from which attempted to save dir name if dirnames set */

      /*  Test for recurse being set removed, since Tandem has no dir concept*/
      /*  recurse into template */
      if ((d = opendir(n)) != NULL)
      {
        while ((e = readd(d)) != NULL) {
          if ((m = procname(e, caseflag)) != ZE_OK)   /* recurse on name */
          {
            if (m == ZE_MISS)
              zipwarn("name not matched: ", e);
            else
              ziperr(m, e);
          }
        }
        closedir(d);
      }
      free((zvoid *)p);
    } /* (s.st_mode & S_IFDIR) == 0) */
    return ZE_OK;
  }

  char *ex2in(x, isdir, pdosflag)
    char *x;                /* external file name */
    int isdir;              /* input: x is a directory */
    int *pdosflag;          /* output: force MSDOS file attributes? */
  /* Convert the external file name to a zip file name, returning the malloc'ed
     string or NULL if not enough memory. */
  {
    char *n;              /* internal file name (malloc'ed) */
    char *t;              /* shortened name */
    int dosflag;
    char *p;               /* pointer to temp area */
    char fname[FILENAME_MAX + 1]= ""; /* file name */
    char ext[EXTENSION_MAX + 1] = ""; /* extension name */
    short extension;    /* does the filename contain an extension */

    dosflag = dosify;  /* default for non-DOS non-OS/2 */

    /* Find starting point in name before doing malloc */
    if (*x == '=')
      t = x + 1;   /* store DEFINE names without the '=' */
    else
      t = x;

    /* Make changes, if any, to the copied name (leave original intact) */

    if (!pathput)
      t = last(t, TANDEM_DELIMITER);

    /* Malloc space for internal name and copy it */
    if ((n = malloc(strlen(t) + 4)) == NULL) /* + 4 for safety */
      return NULL;

    extension = parsename(t,fname,ext);
    t = fname;

    *n= '\0';

    while (*t != '\0') {  /* File part could be sys,vol,subvol or file */
      if (*t == TANDEM_NODE) {    /* System Name */
        strcat(n, INTERNAL_NODE_STR);
        t++;
      }
      else if (*t == TANDEM_DELIMITER) {  /* Volume or Subvol */
             strcat(n, INTERNAL_DELIMITER_STR);
             t++;
           };
      p = strchr(t,TANDEM_DELIMITER);
      if (p == NULL) break;
      strncat(n,t,(p - t));
      t = p;
    }

    strcat(n,t);  /* mop up any left over characters */

    if (extension) {
      strcat(n,DOS_EXTENSION_STR);
      strcat(n,ext);
    };

    if (isdir == 42) return n;      /* avoid warning on unused variable */

    if (dosify)
      msname(n);

    /* Returned malloc'ed name */
    if (pdosflag)
      *pdosflag = dosflag;

    return n;
  }

  void stamp(f, d)
    char *f;                /* name of file to change */
    ulg d;                  /* dos-style time to change it to */
  /* Set last updated and accessed time of file f to the DOS time d. */
  {
    ztimbuf u;            /* argument for utime() */

    /* Convert DOS time to time_t format in u.actime and u.modtime */
    u.actime = u.modtime = dos2unixtime(d);

    utime(f, &u);
  }

  ulg filetime(f, a, n, t)
    char *f;                /* name of file to get info on */
    ulg *a;                 /* return value: file attributes */
    long *n;                /* return value: file size */
    iztimes *t;             /* return value: access and modification time */
  {
    struct stat s;
    char fname[FILENAME_MAX + 1];
    nsk_stat_ov *nsk_ov;

    if (strcmp(f, "-") == 0) {    /* if compressing stdin */
      if (n != NULL) {
        *n = -1L;
      }
    }

    strcpy(fname, f);

    if (stat(fname, &s) != 0) return 0;

    if (a!= NULL) {
      *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWUSR);
      if ((s.st_mode & S_IFMT) == S_IFDIR) {
        *a |= MSDOS_DIR_ATTR;
      }
    }

    if (n!= NULL)
      *n = (s.st_mode & S_IFMT) == S_IFREG ? s.st_size : -1L;

    if (t != NULL) {
      t->atime = s.st_atime;
      t->mtime = s.st_mtime;
      nsk_ov = (nsk_stat_ov *)&s.st_reserved[0];
      t->ctime = nsk_ov->ov.creation_time;
    }

    return unix2dostime(&s.st_mtime);
  }

  int set_extra_field(z, z_utim)
    struct zlist far *z;
    iztimes *z_utim;
    /* create extra field and change z->att if desired */
    /* store full data in local header but just modification time stamp info
       in central header */
  {
    struct stat s;
    nsk_stat_ov *nsk_ov = (nsk_stat_ov *)&s.st_reserved[0];
    nsk_file_attrs *nsk_attr = (nsk_file_attrs *)&nsk_ov->ov.nsk_ef_start;
    char *ext, *cext;
    int lsize, csize;
#ifdef USE_EF_UT_TIME
    char *UTptr, *Uxptr;
#endif /* USE_EF_UT_TIME */

    /* For the Tandem and UT local field including the UID/GID fields, we
       have to stat the file again. */
    if (LSSTAT(z->name, &s))
      return ZE_OPEN;

    z->ext = z->cext = 0;

  #define EB_TANDEM_SIZE 20
  #define EF_TANDEM_SIZE (EB_HEADSIZE + EB_TANDEM_SIZE)

    /* allocate size of buffers to allow Tandem field */
    lsize = EF_TANDEM_SIZE;
    csize = EF_TANDEM_SIZE;

#ifdef USE_EF_UT_TIME

  #define EB_L_UT_SIZE    (EB_HEADSIZE + EB_UT_LEN(3))
  #define EB_C_UT_SIZE    (EB_HEADSIZE + EB_UT_LEN(1))
  #define EB_L_UX2_SIZE   (EB_HEADSIZE + EB_UX2_MINLEN)
  #define EB_C_UX2_SIZE   EB_HEADSIZE
  #define EF_L_UNIX_SIZE  (EB_L_UT_SIZE + EB_L_UX2_SIZE)
  #define EF_C_UNIX_SIZE  (EB_C_UT_SIZE + EB_C_UX2_SIZE)

    /* resize to allow for UT fields */
    lsize += EF_L_UNIX_SIZE;
    csize += EF_C_UNIX_SIZE;

#endif /* USE_EF_UT_TIME */

    if ((z->extra = (char *)malloc(lsize)) == NULL)
      return ZE_MEM;
    ext = z->extra;

    if ((z->cextra = (char *)malloc(csize)) == NULL)
      return ZE_MEM;
    cext = z->cextra;

    /* Place Tandem field first so its on an even boundary */
    *ext++ = *cext++ = 'T';
    *ext++ = *cext++ = 'A';
    *ext++ = *cext++ = (char)EB_TANDEM_SIZE;  /*length of data part of e.f.*/
    *ext++ = *cext++  = 0;

    /* Copy Tandem specific file information */
    memcpy(ext, (char *)nsk_attr, EB_TANDEM_SIZE);
    ext += EB_TANDEM_SIZE;
    z->ext += EF_TANDEM_SIZE;

    /* Copy same data to central field */
    memcpy(cext, (char *)nsk_attr, EB_TANDEM_SIZE);
    cext += EB_TANDEM_SIZE;
    z->cext += EF_TANDEM_SIZE;

#ifdef USE_EF_UT_TIME
    UTptr = ext;
    *ext++  = 'U';
    *ext++  = 'T';
    *ext++  = (char)EB_UT_LEN(3);    /* length of data part of local e.f. */
    *ext++  = 0;
    *ext++  = EB_UT_FL_MTIME | EB_UT_FL_ATIME | EB_UT_FL_CTIME;
    *ext++  = (char)(s.st_mtime);
    *ext++  = (char)(s.st_mtime >> 8);
    *ext++  = (char)(s.st_mtime >> 16);
    *ext++  = (char)(s.st_mtime >> 24);
    *ext++  = (char)(s.st_atime);
    *ext++ = (char)(s.st_atime >> 8);
    *ext++ = (char)(s.st_atime >> 16);
    *ext++ = (char)(s.st_atime >> 24);

    *ext++ = (char)(nsk_ov->ov.creation_time);
    *ext++ = (char)(nsk_ov->ov.creation_time >> 8);
    *ext++ = (char)(nsk_ov->ov.creation_time >> 16);
    *ext++ = (char)(nsk_ov->ov.creation_time >> 24);

    Uxptr = ext;
    *ext++ = 'U';
    *ext++ = 'x';
    *ext++ = (char)EB_UX2_MINLEN;   /* length of data part of local e.f. */
    *ext++ = 0;
    *ext++ = (char)(s.st_uid);
    *ext++ = (char)(s.st_uid >> 8);
    *ext++ = (char)(s.st_gid);
    *ext++ = (char)(s.st_gid >> 8);

    z->ext += EF_L_UNIX_SIZE;

    memcpy(cext, UTptr, EB_C_UT_SIZE);
    cext[EB_LEN] = (char)EB_UT_LEN(1);
    memcpy(cext+EB_C_UT_SIZE, Uxptr, EB_C_UX2_SIZE);
    cext[EB_LEN+EB_C_UT_SIZE] = 0;

    z->cext += EF_C_UNIX_SIZE;
    cext += EF_C_UNIX_SIZE;

#endif /* USE_EF_UT_TIME */

    return ZE_OK;
  }
