/* xpk.c - routines for the master xpk.library. 
   Copyright (C) 1991, 1992, 1993 Kristian Nielsen.

   This file is part of XFH, the compressing file system handler.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.            */

#include "CFS.h"
#include "dossupport.h"

#include <utility/hooks.h>
#include <utility/tagitem.h>

#include <xpk/xpk.h>

#include <string.h>

/* These two include files are changing all the time. Hence the #undef. */
#undef XpkFH

struct Library *XpkBase;

#ifndef min
#define min(a,b) ((a)>(b) ? (b) : (a))
#endif

/* 'Conditional' tag - passed only if condition true. */
#define TAGIF(c,t) ((c)?(t):TAG_IGNORE)

/* Structure for holding an xpk file unpacked in memory. */
struct unpackedxpk {
  struct xpkmemchunk *first,*last,*current;
  LONG currentpos;
};

/* Node in linked list of data buffers. */
struct xpkmemchunk {
  struct xpkmemchunk *next;
  LONG abspos;
  LONG size;
  UBYTE *data;      /* MUST be alloced with dosalloc(). */
};

struct XpkFH {
  struct CFSFH cfsfh;
  struct unpackedxpk *unpackedxpk;
  LONG filelen;
  UBYTE *InBuf;     /* Buffer last returned by inhook. */
  UBYTE *OutBuf;    /* Buffer last returned by outhook. */
  LONG InBufLen;    /* Of InBuf. (ToDo: Not really used...) */
  LONG OutBufLen;   /* Of OutBuf. (ToDo: Not really used...) */
};

struct XpkLock {
  struct CFSLock cfslock;
};



static struct unpackedxpk *newunpackedxpk(void){
  struct unpackedxpk *p;
  
  if(!dalloc(p)) return NULL;
  /* All fields 0 by default. */
  return p;
}

/* NOTE: 'data' should be allocated with dosalloc().
 * NOTE ALSO: Memory block may be larger than 'size' (will be freed by
 * dosfree() ).
 */
static BOOL addxpkmemchunk(struct unpackedxpk *p, UBYTE *data, LONG size )
{
  struct xpkmemchunk *q;
  
  if(!(dalloc(q))) return FALSE;
  q->next=NULL;
  q->size=size;
  q->data=data;
  if(!p->first){
    q->abspos = 0L;
    p->first=p->current=p->last = q;
  }else{
    q->abspos = p->last->abspos+p->last->size;
    p->last->next = q;
    p->last = q;
  }
  return TRUE;
}

static void killunpackedxpk(struct unpackedxpk *p){
  struct xpkmemchunk *q,*r;
  
  for(q=p->first;q;q=r){
    r=q->next;
    dfree(q->data);
    dfree(q);
  }
  dfree(p);
}

static BOOL seekunpackedxpk(struct unpackedxpk *p, LONG abspos){
   struct xpkmemchunk *q;
   
   if(abspos < 0 || !p->first || p->last->abspos+p->last->size < abspos)
      return (BOOL) (abspos == 0);
   /* Special case: seek just beyond end. */
   if(p->last->abspos+p->last->size == abspos){
		debug(("Seeking to last position in file: %ld.\n",abspos));
      p->current = p->last;
      p->currentpos = abspos;
		return DOSTRUE;
   }
   /* Start at current pos. if possible. */
   q = p->current->abspos <= abspos ? p->current : p->first;
   while(q->abspos+q->size <= abspos) q = q->next;
   p->current = q;
   p->currentpos = abspos;
   return DOSTRUE;
}

static BOOL readunpackedxpk(struct unpackedxpk *p, UBYTE *buf, LONG size){
   struct xpkmemchunk *q;
   LONG len,chunkpos;
   
   if(!p->first)
      return (BOOL) (size==0);
   q = p->current;
   while(size>0){
      if(p->last->abspos+p->last->size <= p->currentpos) return FALSE;
      chunkpos=p->currentpos-q->abspos;
      len = min(size,q->size-chunkpos);
      CopyMem(q->data+chunkpos,buf,len);
      size-=len;
      buf+=len;
      p->currentpos+=len;
      if(p->currentpos >= q->abspos+q->size) q=q->next;
      if(q) p->current = q;
   }
   return TRUE;
}



/*------------------------------------------------------------------------*
 * Hooks for Xpk packing / unpacking.
 */

/* Structure for private hook data (glob & Xpkfilehandle). */
struct xpkhookdata {
   glb glob;
   struct XpkFH *fh;
};


/* Free Xpk input buffer if nessesary. */
static void FreeXpkInBuf(struct XpkFH *fh){

   /* debug(("FreeXpkInBuf: Checking if buffers allocated...\n")); */
   if(fh->InBuf){
      /* debug(("FreeXpkInBuf: Freeing buffer: %lx,%ld.\n",fh->InBuf,fh->InBufLen)); */
      dosfree(fh->InBuf);
      fh->InBuf = NULL;
   }
}

/* Allocate Xpk input buffer. */
static UBYTE *AllocXpkInBuf(struct XpkFH *fh, LONG size){

   /* NOTE: I found out (the hard way...) that Xpk expects a buffer
    * 4 bytes larger than requested ... wierd. */
   size+=4;
   FreeXpkInBuf(fh);
   fh->InBufLen=size;
   if(!(fh->InBuf=dosalloc(size))){
      return 0L;
   }
   return fh->InBuf;
}

/* Free Xpk output buffer if nessesary. */
static void FreeXpkOutBuf(struct XpkFH *fh){

   /* debug(("FreeXpkOutBuf: Checking if buffers allocated...\n")); */
   if(fh->OutBuf){
      /* debug(("FreeXpkOutBuf: Freeing buffer: %lx,%ld.\n",fh->OutBuf,fh->OutBufLen)); */
      dosfree(fh->OutBuf);
      fh->OutBuf = NULL;
   }
}

/* Allocate Xpk output buffer. */
static UBYTE *AllocXpkOutBuf(struct XpkFH *fh, LONG size){

   /* NOTE: I found out (the hard way...) that Xpk expects a buffer
    * 4 bytes larger than requested ... wierd. */
   size+=4;
   FreeXpkOutBuf(fh);
   fh->OutBufLen=size;
   if(!(fh->OutBuf=dosalloc(size))){
      return 0L;
   }
   return fh->OutBuf;
}

/* Transfer control to actual hook function. Shields the actual */
/* hook functions from any ugly assembler-like register considerations. */

static LONG __asm __saveds DoXpkHook(register __a0 struct Hook *hook,
register __a1 struct XpkIOMsg *msg)
{
  struct xpkhookdata *hookdata = hook->h_Data;
  LONG ret;
  typedef LONG (*myhooktype)(glb, struct XpkFH *, ULONG, UBYTE **, LONG);
   
  ret = (*(myhooktype)hook->h_SubEntry)
    (hookdata->glob, hookdata->fh, msg->xiom_Type, (UBYTE **) &msg->xiom_Ptr, msg->xiom_Size);
  if(ret) msg->xiom_IOError = hookdata->glob->ioerr;
  return ret;
}

/* Calling conventions for hooks. */
/* ToDo: When docs become available, check against this description. */
/* */
/* XpkIOMsg->xiom_IOError is set only in case of non-zero hook return value. */
/* */
/* Big problem: What is XIO_SEEK supposed to return in XpkIOMsg->Ptr? */
/* The masterlib does the wierdest things... However, it (currently!) */
/* only needs a non-zero return in case of succes. */
/* */
/* XIO_GETBUF/XIO_WRITE in xpkunpackout(): It is assumed that only one */
/* buffer will be needed at any time, so that a second XIO_GETBUF may */
/* free the buffer returned by the first. It is also assumed that */
/* XIO_WRITE may grab the buffer it is writing if it was allocated by */
/* XIO_GETBUF. */
/* */

/* Read() hook for unpacking. */
static LONG xpkunpackin(glb glob, struct XpkFH *fh, ULONG type, UBYTE **buf, LONG size){
   LONG actuallen;
   
/* debug(("UnpackInHook: Type=%ld, Buf=%lx, Len=%ld.\n",type, *buf,size));*/
   switch(type){
     case XIO_READ:
      if(!*buf){
         if(!(*buf=AllocXpkInBuf(fh,size))) return XPKERR_NOMEM;
      }
      if( (actuallen = xRead(glob, fh->cfsfh.xfh, *buf, size)) != size){
         return actuallen >= 0 ? XPKERR_TRUNCATED : XPKERR_IOERRIN;
      }
      break;
     case XIO_WRITE:
      debug(("Error: xpkunpackin(): Xpk attemps to XIO_WRITE.\n"));
      return XPKERR_NOFUNC;
     case XIO_FREE:
     case XIO_ABORT:
      FreeXpkInBuf(fh); 
      break;
     case XIO_GETBUF:
      if(!(*buf=AllocXpkInBuf(fh,size))) return XPKERR_NOMEM;
      break;
     case XIO_SEEK:
      if( xSeek( glob, fh->cfsfh.xfh, size, OFFSET_CURRENT)<0 ){
         return XPKERR_IOERROUT;
      }
      *buf = (APTR) 4L;   /* VERY strange... */
      break;
     case XIO_TOTSIZE:
      /* debug(("..... XIO_TOTSIZE: no action...\n")); */
      break;
     default:
      debug(("*** PANIC: xpkunpackin(): Unknown type of action requested.\n"));
      return XPKERR_NOFUNC;
   }
   
   return XPKERR_OK;
}


/* Write() hook for unpacking. */
static LONG xpkunpackout(glb glob, struct XpkFH *fh, ULONG type, UBYTE **buf, LONG size){
   
/* debug(("UnpackOutHook: Type=%ld, Buf=%lx, Len=%ld.\n",type,*buf,size)); */
   switch(type){
     case XIO_READ:
      debug(("Error: xpkunpackout(): Xpk attemps to XIO_READ.\n"));
      return XPKERR_NOFUNC;
     case XIO_WRITE:
      if( *buf == fh->OutBuf /* && size == fh->OutBufLen */ ){
         /* Writing our previously allocated buffer. Just add it to */
         /* the filehandle, and mark it as used. */
			/* NOTE: because of safety margin, the added memory block is */
			/* larger than nesseeary. */

         /* debug(("Adding previously allocated buffer to filehandle: %lx\n",*buf)); */
         if(!addxpkmemchunk(fh->unpackedxpk,*buf,size)){
            debug(("Error: xpkunpackout(): Cannot add data.\n"));
            return XPKERR_NOMEM;
         }
         fh->OutBuf = NULL;
         fh->OutBufLen = 0L;
      }else{
         UBYTE *newbuf;
         
         /* debug(("Copying data into filehandle.\n")); */
         if( !(newbuf = dosalloc(size)) ){
            debug(("Error: xpkunpackout(): No memory.\n"));
            return XPKERR_NOMEM;
         }
         CopyMem(*buf,newbuf,size);
         if(!addxpkmemchunk(fh->unpackedxpk,newbuf,size)){
            debug(("Error: xpkunpackout(): Cannot add data.\n"));
            dosfree(newbuf);
            return XPKERR_NOMEM;
         }
      }
      break;
     case XIO_FREE:
     case XIO_ABORT:
      FreeXpkOutBuf(fh);
      break;
     case XIO_GETBUF:
      if( !(*buf = AllocXpkOutBuf(fh,size)) ){
         debug(("Error: xpkunpackout(): Cannot get buffer for xpk.\n"));
         return XPKERR_NOMEM;
      }
      /* debug(("xpkunpackout()/XIO_GETBUF: returning buffer %lx\n",*buf)); */
      break;
     case XIO_SEEK:
        /* Strange... Surely this code is wrong? I mean, there's no file
         * handle, is there? Commented out for now. */
/*      if( xSeek( glob, fh->cfsfh.xfh, size, OFFSET_CURRENT)<0 ){ */
/*         return XPKERR_IOERROUT; */
/*      } */
/*      *buf = (APTR) 4L;   /* VERY strange... */
      debug(("Error: xpkunpackout(): Xpk attempts to XIO_SEEK.\n"));
      return XPKERR_NOFUNC;
/*      break; */
     case XIO_TOTSIZE:
      /* debug(("..... XIO_TOTSIZE: no action...\n")); */
      break;
     default:
      debug(("*** PANIC: xpkunpackout(): Unknown type of action requested.\n"));
      return XPKERR_NOFUNC;
   }
   return XPKERR_OK;
}


/* Hooks for Xpk packing.
 * Currently, these use simple XpkFH file handles. However, I'm hoping
 * to eventually make them use async I/O.
 * NOTE BIEN: these XpkFH are FAKE, and cannot be used as such safely.
 * Specifically, the cfsfh is not valid (though cfsfh.xfh is).
 */

/* NOTE: xpkpackin() / xpkpackout() are also used for unpacking. */
/* Read() hook for packing. */
static LONG xpkpackin(glb glob, struct XpkFH *fh, ULONG type, UBYTE **buf, LONG size){
   LONG actuallen;
   
/* debug(("PackInHook: Type=%ld, Buf=%lx, Len=%ld.\n",type, *buf,size));*/
   switch(type){
     case XIO_READ:
      if(!*buf){
         if(!(*buf=AllocXpkInBuf(fh,size))) return XPKERR_NOMEM;
      }
      if( (actuallen = xRead(glob, fh->cfsfh.xfh, *buf, size)) != size){
         return actuallen >= 0 ? XPKERR_TRUNCATED : XPKERR_IOERRIN;
      }
      break;
     case XIO_WRITE:
      debug(("Error: xpkpackin(): Xpk attemps to XIO_WRITE.\n"));
      return XPKERR_NOFUNC;
     case XIO_FREE:
     case XIO_ABORT:
      FreeXpkInBuf(fh); 
      break;
     case XIO_GETBUF:
      if(!(*buf=AllocXpkInBuf(fh,size))) return XPKERR_NOMEM;
      break;
     case XIO_SEEK:
      if( xSeek( glob, fh->cfsfh.xfh, size, OFFSET_CURRENT)<0 ){
         return XPKERR_IOERROUT;
      }
      *buf = (APTR) 4L;   /* VERY strange... */
      break;
     case XIO_TOTSIZE:
      /* debug(("..... XIO_TOTSIZE: no action...\n")); */
      break;
     default:
      debug(("*** PANIC: xpkpackin(): Unknown type of action requested.\n"));
      return XPKERR_NOFUNC;
   }
   
   return XPKERR_OK;
}


/* Write() hook for packing. */
static LONG xpkpackout(glb glob, struct XpkFH *fh, ULONG type, UBYTE **buf, LONG size){
   
/* debug(("PackOutHook: Type=%ld, Buf=%lx, Len=%ld.\n",type,*buf,size));*/
   switch(type){
     case XIO_READ:
      debug(("Error: xpkpackout(): Xpk attemps to XIO_READ.\n"));
      return XPKERR_NOFUNC;
     case XIO_WRITE:
      if(xWrite(glob, fh->cfsfh.xfh, *buf, size) != size){
         return XPKERR_IOERROUT;
      }
      break;
     case XIO_FREE:
     case XIO_ABORT:
      FreeXpkOutBuf(fh);
      break;
     case XIO_GETBUF:
      if( !(*buf = AllocXpkOutBuf(fh,size)) ){
         debug(("Error: xpkpackout(): Cannot get buffer for xpk.\n"));
         return XPKERR_NOMEM;
      }
      /* debug(("xpkpackout()/XIO_GETBUF: returning buffer %lx\n",*buf)); */
      break;
     case XIO_SEEK:
      if( xSeek( glob, fh->cfsfh.xfh, size, OFFSET_CURRENT)<0 ){
         return XPKERR_IOERROUT;
      }
      *buf = (APTR) 4L;   /* VERY strange... */
      break;
     case XIO_TOTSIZE:
      /* debug(("..... XIO_TOTSIZE: no action...\n")); */
      break;
     default:
      debug(("*** PANIC: xpkpackout(): Unknown type of action requested.\n"));
      return XPKERR_NOFUNC;
   }
   return XPKERR_OK;
}

/*------------------------------------------------------------------------*
 * Low-level interface to Xpk functions.
 */


/* The following code is there to support pre-2.0 OS versions. The
 * problem is that Xpk insists on doing OpenLibrary() itself, which
 * causes the dreaded ASYNCPKT guru.
 */
 
struct xpkunpackmsg{
   struct Message msg;
   void (*func)();
   glb glob;
   struct TagItem *tags;
   LONG result;
};

struct xpkexammsg{
   struct Message msg;
   void (*func)();
   glb glob;
   struct XpkFib *fib;
   struct TagItem *tags;
   LONG result;
};

struct xpkpackmsg{
   struct Message msg;
   void (*func)();
   glb glob;
   struct TagItem *tags;
   LONG result;
};

static void __asm CallXpkUnpackTags(register __a0 struct xpkunpackmsg *msg){
   struct MsgPort *port;
   
   debug(("Calling XpkUnpack(%lx)...", msg->tags ));
   port = msg->glob->ioport;
   if(msg->glob->ioport=CreatePort(NULL,0L)){
      msg -> result = XpkUnpack(msg->tags);
      DeletePort(msg->glob->ioport);
   }else{
      debug(("(no port)"));
      msg->result = XPKERR_NOMEM;
   }
   msg->glob->ioport = port;
   debug(("=%ld\n",msg->result));
}


static void __asm CallXpkExamine(register __a0 struct xpkexammsg *msg){
   struct MsgPort *port;
   
   debug(("Calling XpkExamine(%lx,%lx)...", msg->fib, msg->tags ));
   port = msg->glob->ioport;
   if(msg->glob->ioport=CreatePort(NULL,0L)){
      msg -> result = XpkExamine(msg->fib, msg->tags);
      DeletePort(msg->glob->ioport);
   }else{
      debug(("(no port)"));
      msg->result = XPKERR_NOMEM;
   }
   msg->glob->ioport = port;
   debug(("=%ld\n",msg->result));
}


static void __asm CallXpkPackTags(register __a0 struct xpkpackmsg *msg){
   struct MsgPort *port;
   
   debug(("Calling XpkPack(%lx)...", msg->tags ));
   port = msg->glob->ioport;
   if(msg->glob->ioport=CreatePort(NULL,0L)){
      msg -> result = XpkPack(msg->tags);
      DeletePort(msg->glob->ioport);
   }else{
      debug(("(no port)"));
      msg->result = XPKERR_NOMEM;
   }
   msg->glob->ioport = port;
   debug(("=%ld\n",msg->result));
}


static LONG MyXpkUnpackTags( glb glob, ULONG firsttag, ... ){
   /* Cannot call xpk from a Task (or handler) before KS 2.0. */
   if( ((struct Library *)glob->DOSBase)->lib_Version >= 36){
      return XpkUnpack( (struct TagItem *)&firsttag );
   }else{
      struct xpkunpackmsg msg,*msg2;
      extern void DoDOSSeg();
      struct MsgPort *procid;
   
      msg.msg.mn_Node.ln_Succ=NULL;
      msg.msg.mn_Node.ln_Pred=NULL;
      msg.msg.mn_Node.ln_Name=NULL;
      msg.msg.mn_Node.ln_Type=NT_MESSAGE;
      msg.msg.mn_Node.ln_Pri=0;
      msg.msg.mn_ReplyPort=glob->xpkport;
      msg.msg.mn_Length=sizeof(msg);
      msg.func=(void (*)()) CallXpkUnpackTags;
      msg.glob = glob;
      msg.tags = (struct TagItem *)&firsttag;

      if(!(procid=CreateProc   /* Lets pray that 10K stack is enough... */
        ("XpkUnpack()",glob->mytask->tc_Node.ln_Pri,(BPTR)((ULONG)DoDOSSeg>>2),10000L)))
        return XPKERR_NOMEM;
   
      PutMsg(procid,(struct Message *)&msg);
      do WaitPort(glob->xpkport);
      while(!(msg2=(struct xpkunpackmsg *)GetMsg(glob->xpkport)));

#ifdef DEBUG
      if(msg2!=&msg)
         KPrintF("ERROR: bogus return message: &msg=%lx msg2=%lx\n",&msg,msg2);
#endif
      return msg2->result;
   }
}

static LONG MyXpkExamine( glb glob, struct XpkFib *fib, ULONG firsttag, ... ){
   /* Cannot call xpk from a Task (or handler) before KS 2.0. */
   if( ((struct Library *)glob->DOSBase)->lib_Version >= 36){
      return XpkExamine( fib, (struct TagItem *)&firsttag );
   }else{
      struct xpkexammsg msg,*msg2;
      extern void DoDOSSeg();
      struct MsgPort *procid;
   
      msg.msg.mn_Node.ln_Succ=NULL;
      msg.msg.mn_Node.ln_Pred=NULL;
      msg.msg.mn_Node.ln_Name=NULL;
      msg.msg.mn_Node.ln_Type=NT_MESSAGE;
      msg.msg.mn_Node.ln_Pri=0;
      msg.msg.mn_ReplyPort=glob->xpkport;
      msg.msg.mn_Length=sizeof(msg);
      msg.func=(void (*)()) CallXpkExamine;
      msg.glob = glob;
      msg.fib = fib;
      msg.tags = (struct TagItem *)&firsttag;

      if(!(procid=CreateProc   /* Lets pray that 10K stack is enough... */
        ("XpkExamine()",glob->mytask->tc_Node.ln_Pri,(BPTR)((ULONG)DoDOSSeg>>2),10000L)))
        return XPKERR_NOMEM;
   
      PutMsg(procid,(struct Message *)&msg);
      do WaitPort(glob->xpkport);
      while(!(msg2=(struct xpkexammsg *)GetMsg(glob->xpkport)));

#ifdef DEBUG
      if(msg2!=&msg)
         KPrintF("ERROR: bogus return message: &msg=%lx msg2=%lx\n",&msg,msg2);
#endif
      return msg2->result;
   }
}

static LONG MyXpkPackTags( glb glob, ULONG firsttag, ... ){
   /* Cannot call Xpk from a Task (or handler) before KS 2.0. */
   if( ((struct Library *)glob->DOSBase)->lib_Version >= 36){
      return XpkPack( (struct TagItem *)&firsttag );
   }else{
      struct xpkpackmsg msg,*msg2;
      extern void DoDOSSeg();
      struct MsgPort *procid;
   
      msg.msg.mn_Node.ln_Succ=NULL;
      msg.msg.mn_Node.ln_Pred=NULL;
      msg.msg.mn_Node.ln_Name=NULL;
      msg.msg.mn_Node.ln_Type=NT_MESSAGE;
      msg.msg.mn_Node.ln_Pri=0;
      msg.msg.mn_ReplyPort=glob->xpkport;
      msg.msg.mn_Length=sizeof(msg);
      msg.func=(void (*)())CallXpkPackTags;
      msg.glob = glob;
      msg.tags = (struct TagItem *)&firsttag;

      if(!(procid=CreateProc   /* Lets pray that 10K stack is enough... */
        ("XpkPack()",glob->mytask->tc_Node.ln_Pri,(BPTR)((ULONG)DoDOSSeg>>2),10000L)))
        return XPKERR_NOMEM;
   
      PutMsg(procid,(struct Message *)&msg);
      do WaitPort(glob->xpkport);
      while(!(msg2=(struct xpkpackmsg *)GetMsg(glob->xpkport)));

#ifdef DEBUG
      if(msg2!=&msg)
         KPrintF("ERROR: bogus return message: &msg=%lx msg2=%lx\n",&msg,msg2);
#endif
      return msg2->result;
   }
}


/* This function opens an existing file in the xpk format, unpacking
 * it to memory with the help of the xpk.library. Note that due to
 * limitation in the current interface of xpk.library, the whole file
 * must be unpacked at once, which is more speed efficient (and by far
 * the easiest to implement), but requires rather a lot of memory
 * (potentially requiring the data to reside 2-3 times in main memory
 * simultaneously) and could result in memory fragmentation.
 */
struct XpkFH *XpkOpenOldFile( glb glob, struct FileHandle *xfh ){
   struct XpkFH *fh;
   LONG res;
   LONG inlen;
   struct Hook inhook,outhook;
   struct xpkhookdata indata,outdata;
   
   if(!dalloc(fh)){
      OUTOFMEM;
      return NULL;
   }
   fh->cfsfh.objtype = XPKOBJECT;
   fh->cfsfh.mode = MODE_OLDFILE;
   fh->cfsfh.xfh = xfh;
   fh->cfsfh.f = &Xpkfunc;
   /* fh->cfsfh.filename and fh->cfsfh.parent NULL by default. */
   
   if(!(fh->unpackedxpk=newunpackedxpk())){
      OUTOFMEM;
      dfree(fh);
      return NULL;
   }
   
   inhook.h_Entry = (ULONG (*)())DoXpkHook;
   inhook.h_SubEntry = (ULONG (*)())xpkunpackin;
   indata.glob=glob;
   indata.fh=fh;
   inhook.h_Data = &indata;
   outhook.h_Entry = (ULONG (*)())DoXpkHook;
   outhook.h_SubEntry = (ULONG (*)())xpkunpackout;
   outdata.glob=glob;
   outdata.fh=fh;
   outhook.h_Data = &outdata;

   /* Get length of file, if possible. */
   inlen = xFileSizeXfh( glob, xfh );

   glob->ioerr = 0L;          /* Set by hookfuncs if adequate. */
   res = MyXpkUnpackTags( glob,
            XPK_InHook, &inhook,
            XPK_OutHook, &outhook,
            XPK_Password, glob->xpkpassword,
            TAGIF(inlen!=-1L,XPK_InLen), inlen,
            TAGIF(glob->xpksetpri,XPK_TaskPri), glob->xpkpri,
            TAG_DONE
         );
	debug(("XpkUnPack() returned: %ld\n",res));
   FreeXpkInBuf(fh);
   FreeXpkOutBuf(fh);
   
   if(res != XPKERR_OK){
      if(!glob->ioerr){
         /* ToDo: Since no adequate AmigaDOS error code exists, we */
         /* should perhaps open a requester to inform the user of */
         /* the problem (showing an XpkErr)? */
         glob->ioerr = ERROR_OBJECT_WRONG_TYPE;  /* More likely out of memory. */
      }
      killunpackedxpk(fh->unpackedxpk);
      dfree(fh);
      return NULL;
   }
   if(fh->unpackedxpk->first)
      fh->filelen = fh->unpackedxpk->last->abspos 
                  + fh->unpackedxpk->last->size; /* else 0 by default. */
   return fh;
}


/* Perform a XpkExamine() on an UFS file handle. */
BOOL XpkExamine_FH( glb glob, struct FileHandle *xfh, struct XpkFib *fib ){
   LONG res;
   LONG inlen;
   struct XpkFH *fh;
   struct Hook inhook;
   struct xpkhookdata indata;
   
   if(!dalloc(fh)){ /* NOTE: NOT a real xpkfilehandle (only InBuf is used). */
      OUTOFMEM;
      return FALSE;
   }
   fh->cfsfh.xfh = xfh;

   inhook.h_Entry = (ULONG (*)())DoXpkHook;
   inhook.h_SubEntry = (ULONG (*)())xpkunpackin;
    indata.glob=glob;
    indata.fh=fh;
   inhook.h_Data = &indata;

   /* Get length of file, if possible. */
   inlen = xFileSizeXfh( glob, xfh );
   
   glob->ioerr = 0L;          /* Set by hookfuncs if adequate. */
   res = MyXpkExamine( glob, fib,
            XPK_InHook,&inhook,
            TAGIF(inlen!=-1L,XPK_InLen), inlen,
            TAGIF(glob->xpksetpri,XPK_TaskPri), glob->xpkpri,
            TAG_DONE
         );
	debug(("XpkExamine() returned: %ld\n",res));
   FreeXpkInBuf(fh);
   dfree(fh);
      /* Need a better error message here. */
   if( res && !glob->ioerr ) glob->ioerr = ERROR_OBJECT_WRONG_TYPE;
	return (BOOL)( res==XPKERR_OK );
}	


/* This is the function that does the actual compression (using xpk) from
 * one file handle to another.
 */
static BOOL XpkPackFH2FH(glb glob, struct FileHandle *srcxfh,
                         LONG inlen, struct FileHandle *dstxfh){
   struct XpkFH *srcxpkfh,*dstxpkfh;  /* NOTE: these are FAKE/invalid. */
   LONG res;
   LONG outlen;                     /* Dummy variable. */
   struct Hook inhook,outhook;
   struct xpkhookdata indata,outdata;
   
   /* Create the two fake XpkFH filehandles for the hooks. */
   if(!dalloc(srcxpkfh)){
      OUTOFMEM;
      return FALSE;
   }
   if(!dalloc(dstxpkfh)){
      OUTOFMEM;
      dfree(srcxpkfh);
      return FALSE;
   }
   srcxpkfh->cfsfh.xfh = srcxfh;   /* Buffer pointers NULL by default. */
   dstxpkfh->cfsfh.xfh = dstxfh;   /* Buffer pointers NULL by default. */
   
   /* Set up hooks. */
   inhook.h_Entry = (ULONG (*)())DoXpkHook;
   inhook.h_SubEntry = (ULONG (*)())xpkpackin;
   indata.glob=glob;
   indata.fh=srcxpkfh;
   inhook.h_Data = &indata;
   outhook.h_Entry = (ULONG (*)())DoXpkHook;
   outhook.h_SubEntry = (ULONG (*)())xpkpackout;
   outdata.glob=glob;
   outdata.fh=dstxpkfh;
   outhook.h_Data = &outdata;

   glob->ioerr = 0L;          /* Set by hookfuncs if adequate. */
   res = MyXpkPackTags( glob,
      XPK_InHook,&inhook,
      XPK_OutHook,&outhook,
      XPK_InLen,inlen,
      XPK_PackMethod,glob->packmode,
      XPK_StepDown,glob->stepdown,
      XPK_Password,glob->xpkpassword,
      XPK_GetOutLen,&outlen, /* NOTE: not used, buf xpk.doc says needed. */
      TAGIF(glob->xpksetpri,XPK_TaskPri), glob->xpkpri,
      TAG_DONE
    );
	debug(("XpkPack() returned: %ld\n",res));
   
   FreeXpkInBuf(srcxpkfh);
   FreeXpkOutBuf(dstxpkfh);
   dfree(srcxpkfh);
   dfree(dstxpkfh);

   if(res != XPKERR_OK){
      if(!glob->ioerr){
         /* ToDo: Since no adequate AmigaDOS error code exists, we */
         /* should perhaps open a requester to inform the user of */
         /* the problem (showing an XpkErr)? */
         glob->ioerr = ERROR_OBJECT_WRONG_TYPE;  /* More likely out of memory. */
      }
      return FALSE;
   }
   return TRUE;
}


/* This is the function that does the actual compression (using xpk) from
 * one file handle to another.
 */
static BOOL XpkUnPackFH2FH(glb glob, struct FileHandle *srcxfh,
                         LONG inlen, struct FileHandle *dstxfh){
   struct XpkFH *srcxpkfh,*dstxpkfh;  /* NOTE: these are FAKE/invalid. */
   LONG res;
   struct Hook inhook,outhook;
   struct xpkhookdata indata,outdata;
   
   /* Create the two fake XpkFH filehandles for the hooks. */
   if(!dalloc(srcxpkfh)){
      OUTOFMEM;
      return FALSE;
   }
   if(!dalloc(dstxpkfh)){
      OUTOFMEM;
      dfree(srcxpkfh);
      return FALSE;
   }
   srcxpkfh->cfsfh.xfh = srcxfh;   /* Buffer pointers NULL by default. */
   dstxpkfh->cfsfh.xfh = dstxfh;   /* Buffer pointers NULL by default. */
   
   /* Set up hooks. */
   inhook.h_Entry = (ULONG (*)())DoXpkHook;
   inhook.h_SubEntry = (ULONG (*)())xpkpackin;
   indata.glob=glob;
   indata.fh=srcxpkfh;
   inhook.h_Data = &indata;
   outhook.h_Entry = (ULONG (*)())DoXpkHook;
   outhook.h_SubEntry = (ULONG (*)())xpkpackout;
   outdata.glob=glob;
   outdata.fh=dstxpkfh;
   outhook.h_Data = &outdata;

   glob->ioerr = 0L;          /* Set by hookfuncs if adequate. */
   res = MyXpkUnpackTags( glob,
      XPK_InHook,&inhook,
      XPK_OutHook,&outhook,
      XPK_InLen,inlen,
      XPK_Password,glob->xpkpassword,
      TAGIF(glob->xpksetpri,XPK_TaskPri), glob->xpkpri,
      TAG_DONE
    );
   debug(("XpkUnPack() returned: %ld\n",res));
   
   FreeXpkInBuf(srcxpkfh);
   FreeXpkOutBuf(dstxpkfh);
   dfree(srcxpkfh);
   dfree(dstxpkfh);

   if(res != XPKERR_OK){
      if(!glob->ioerr){
         /* ToDo: Since no adequate AmigaDOS error code exists, we */
         /* should perhaps open a requester to inform the user of */
         /* the problem (showing an XpkErr)? */
         glob->ioerr = ERROR_OBJECT_WRONG_TYPE;  /* More likely out of memory. */
      }
      return FALSE;
   }
   return TRUE;
}


/* NOTE BIEN: This function preserves the lock! */
struct XpkFH *XpkOpenOldFileFromCopyOfLock( glb glob, struct XpkLock *lock ){
   struct FileHandle *xfh;
   struct XpkFH *fh;
   
   if(!(xfh = xOpenFromCopyOfLock(glob, lock->cfslock.xlock))){
      debug(("Unable to xOpenFromLock(): %ld.\n",glob->ioerr));
      return NULL;
   }
   fh = XpkOpenOldFile( glob, xfh );
   if(!fh){
      LONG saveioerr = glob->ioerr;
      xClose(glob,xfh);
      glob->ioerr = saveioerr;
   }
   return fh;
}


/* Check if a given filehandle belongs to an Xpk file. glob->ioerr
 * set and FALSE returned in case of error.
 */

BOOL IsXpkFile( glb glob, struct FileHandle *xfh ){
   struct XpkFib fib;
   
   if(!XpkExamine_FH(glob, xfh, &fib) ){
      debug(("Error: IsXpkFile: XpkExamine_FH returned error\n"));
      return FALSE;
   }
   else if(fib.xf_Type == XPKTYPE_PACKED )
      return TRUE;
   else
   {
      glob->ioerr = 0L;
      return FALSE;
   }
}


/* This is the main entry to the automatic compression, and is the
 * function passed to TransFormFile().
 */
BOOL PackFile2File(glb glob, struct FileHandle *srcxfh,
                   struct FileHandle *dstxfh, void *dummy){
   LONG srcsize;
   
   srcsize = xGetFileSize(glob, srcxfh);
   if(srcsize == -1L){
      debug(("Error: PackFile2File(): Could not obtain file length.\n"));
      return FALSE;
   }
   return XpkPackFH2FH(glob, srcxfh, srcsize, dstxfh);
}


/* This function is passed to TransFormFile() to unpack a file to another
 * file (to handle Write() to an Xpk file).
 */
BOOL UnPackFile2File(glb glob, struct FileHandle *srcxfh,
                     struct FileHandle *dstxfh, void *dummy){
   LONG srcsize;
   
   srcsize = xGetFileSize(glob, srcxfh);
   if(srcsize == -1L){
      debug(("Error: UnPackFile2File(): Could not obtain file length.\n"));
      return FALSE;
   }
   return XpkUnPackFH2FH(glob, srcxfh, srcsize, dstxfh);
}


/*------------------------------------------------------------------------*
 * Virtual functions for Open(), Read(), Lock() etc.
 */

LONG Xpk_Read( glb glob, struct XpkFH *fh, UBYTE *buf, LONG len ){

   if( fh->unpackedxpk->currentpos+len > fh->filelen )
      len = fh->filelen - fh->unpackedxpk->currentpos;
   if( len <= 0 ) return 0L;
   
   if(!readunpackedxpk(fh->unpackedxpk, buf, len )){
      return -1L;
   }
   return len;
}


LONG Xpk_Seek( glb glob, struct XpkFH *fh, LONG pos, LONG offset ){
	LONG abspos;
	LONG oldpos = fh->unpackedxpk->currentpos;
   
   if( offset != OFFSET_BEGINNING &&
       offset != OFFSET_CURRENT   &&
       offset != OFFSET_END ){
      debug(("Error: Xpk: Bad offset value for Seek(): %ld\n",offset));
      glob->ioerr = ERROR_ACTION_NOT_KNOWN;
      return -1L;
   }
   abspos = abs_seek_pos( fh->unpackedxpk->currentpos, fh->filelen, pos, offset );
   if( abspos < 0 || abspos > fh->filelen ){
      debug(("Error: Xpk: Bad abs. pos. in Seek(): %ld\n",abspos));
      glob->ioerr = ERROR_SEEK_ERROR;
      return -1L;
   }
	debug(("XpkSeek(): fh=%lx, pos=%ld, offset=%ld, abspos=%ld\n",fh,pos,offset,abspos));
   seekunpackedxpk(fh->unpackedxpk,abspos);
   
   return oldpos;
}


BOOL Xpk_Close( glb glob, struct XpkFH *fh ){
  BOOL res;
   
  if(!fh->cfsfh.xfh){
    debug(("Xpk_Close(): Bad fh.\n"));
    glob->ioerr = ERROR_OBJECT_WRONG_TYPE;
    return FALSE;
  }
  res = xClose( glob, fh->cfsfh.xfh );
  if( res ){
    killunpackedxpk(fh->unpackedxpk);
    XObjUnStuffFH(glob, &fh->cfsfh);
    dfree(fh);
  }else{
    debug(("ERROR: Xpk: xClose() returned false - mem not freed.\n"));
  }
  return res;
}


/* Xpk_Write() needs to do some magic to prepare the file for writing. The
 * idea is to replace the file (on disk) with the unpacked data, then make
 * the file handle look like a CFSFH.
 *
 * One possibility would be to write the data in memory. However, since
 * the file could have changed since this Open(), it's safer to
 * uncompress the file again.
 */
LONG Xpk_Write(glb glob, struct XpkFH *fh, UBYTE *buf, LONG len){
  BOOL res;
  char *filename = fh->cfsfh.filename;
  struct FileLock *xlock = fh->cfsfh.parent->xlock;
  struct FileHandle *xfh = fh->cfsfh.xfh;
  LONG currentpos;

  if(!glob->allowappend){
    debug(("Xpk_Write(): Appendmode not set - failing.\n"));
    glob->ioerr = ERROR_ACTION_NOT_KNOWN;
    return -1L;
  }
  if(!xfh){
    debug(("XpkWrite(): Bad fh.\n"));
    glob->ioerr = ERROR_OBJECT_WRONG_TYPE;
    return -1L;
  }
  if(!filename){
    debug(("Error: Xpk_Write(): no name info.\n"));
    glob->ioerr = ERROR_ACTION_NOT_KNOWN;
    return -1L;
  }
  debug(("Attempting to uncompress file %s.\n",filename));
  if(!xChangeMode(glob, CHANGE_FH, xfh, MODE_NEWFILE)){
    debug(("Error: Xpk_Write(): xChangeMode(): %ld.\n",glob->ioerr));
    return -1L;
  }
  if(-1L == xSeek(glob, xfh, 0L, OFFSET_BEGINNING)){
    debug(("Error: Xpk_Write(): Could not seek to start: %ld.\n",glob->ioerr));
    return -1L;
  }
  res = TransformXFH(glob, xfh, xlock, filename, UnPackFile2File, NULL);
  debug(("TransformFile() returned: %ld.\n",res));
  fh->cfsfh.xfh = xfh = NULL;   /* The handle was closed by TransformFile(). */
  if(!res){
    SAVEIOERR;
    /* Bad luck! The uncompress didn't work. Since our xfh is gone, we'll
     * have to do some magic to save as much as possible.
     */
    if( !(xfh=xOpen(glob, xlock, filename, fh->cfsfh.mode)) ){
      debug(("Error: File %s could not be re-opened: %d.\n",filename,glob->ioerr));
      /* Nothing to do about it, really... */
    }
    RESTIOERR;
    return -1L;
  }
  if( !(xfh=xOpen(glob, xlock, filename, fh->cfsfh.mode)) ){
    debug(("Error: File %s could not be re-opened: %d.\n",filename,glob->ioerr));
    /* Nothing to do about it, really... */
    return -1L;
  }
  fh->cfsfh.xfh = xfh;
  currentpos = fh->unpackedxpk->currentpos;
  killunpackedxpk(fh->unpackedxpk);
  if(!glob->compressreadwrite) XObjUnStuffFH(glob, &fh->cfsfh);
  XObjStealXpkFH(glob, &fh->cfsfh);
  if(-1L == xSeek(glob, xfh, currentpos, OFFSET_BEGINNING)){
    debug(("Error: Xpk_Write(): Could not seek to right pos: %ld.\n",glob->ioerr));
    return -1L;
  }else{
    return XObjWrite(glob, &fh->cfsfh, buf, len);
  }
}


struct XpkLock *XpkMakeLock( glb glob, struct FileLock *xlock, LONG mode ){
	struct XpkLock *newlock;
	
	if( !dalloc(newlock) ){
		OUTOFMEM;
		return NULL;
	}
	newlock->cfslock.objtype = XPKOBJECT;
	newlock->cfslock.mode = mode;
	newlock->cfslock.xlock = xlock;
	newlock->cfslock.f = &Xpkfunc;
   newlock->cfslock.refcount = 0;    /* NOTE: NOT used for XpkObject's. */
	return newlock;
}


struct XpkLock *XpkDupLock( glb glob, struct XpkLock *lock ){
   struct XpkLock *newlock;

   /* XPKOBJECT locks have their xLocks xDupLock'ed, and the rest of
    * the fields are just copied vanilla.
    */
   if( !dalloc(newlock) ){
      OUTOFMEM;
      return 0L;
   }
   *newlock = *lock;
   newlock->cfslock.refcount = 0L;   /* Again: refcount is not used. */
   if( !(newlock->cfslock.xlock = xDupLock(glob, lock->cfslock.xlock)) ){
      dfree(newlock);
      return 0L;
   }
   return newlock;   
}


/* NOTE BIEN: The parent lock is a XOBJECT, not a XPKOBJECT! */
struct CFSLock *XpkParentDir( glb glob, struct XpkLock *lock ){
	struct FileLock *xlock;
	struct CFSLock *newlock;
	
	if( !(xlock = xParentDir(glob, lock->cfslock.xlock)) ){
		return 0L;
	}
	newlock = XObjMakeLock( glob, xlock, ACCESS_READ );
	if( !newlock ){
		LONG saveioerr = glob->ioerr;
		xUnLock(glob, xlock);
		glob->ioerr = saveioerr;
	}
	return newlock;
}


/* NOTE BIEN: The parent lock is a XOBJECT, not a XPKOBJECT! */
struct CFSLock *XpkParentFH( glb glob, struct XpkFH *fh ){
	
	return XObjParentFH(glob, &fh->cfsfh);
}


BOOL XpkUnLock( glb glob, struct XpkLock *lock ){
	BOOL err;

   if(lock->cfslock.refcount){
      /* This should NOT have happened! */
      debug(("\n\n***PANIC***: XpkUnLock: Nonzero refcount in lock (%ld).\n",lock->cfslock.refcount));
      return DOSFALSE;
   }
	err = xUnLock(glob, lock->cfslock.xlock);
      
	/* Now, what if the UnLock fails? I guess it's best to return FALSE
	 * and let the lock live.
	 */
	if(!err) return DOSFALSE;
	dfree(lock);
	return DOSTRUE;
}


BOOL XpkSameLock(glb glob, struct XpkLock *l1, struct XpkLock *l2 ){
	return (BOOL) (xSameLock( glob, l1->cfslock.xlock, l2->cfslock.xlock ) == LOCK_SAME);
}

LONG ATUL ( char *Ptr ){
	UWORD Index;
	LONG LW;

	for (Index=0,LW=0L;Index<8;Index++)
	  LW=(LW<<4)+(LONG)(*Ptr++-'A');

	return LW;
}

BOOL XpkFastModifyFIB( struct FileInfoBlock *FIB ){

	if( (strncmp( FIB->fib_Comment, XFH_ID, 5 ) != 0) ||
            (strlen( FIB->fib_Comment ) != 50) ) return FALSE;
	debug(("XpkFastModifyFIB: header found: %s\n",FIB->fib_Comment));

	FIB->fib_Comment[0] = '\0';
	if ( (ATUL( &FIB->fib_Comment[6] ) == FIB->fib_Date.ds_Days) &&
	     (ATUL( &FIB->fib_Comment[15] ) == FIB->fib_Date.ds_Minute) &&
	     (ATUL( &FIB->fib_Comment[24] ) == FIB->fib_Date.ds_Tick) &&
	     (ATUL( &FIB->fib_Comment[33] ) == FIB->fib_Size) ){
	    FIB->fib_Size = ATUL( &FIB->fib_Comment[42] );
	    debug(("XpkFastModifyFIB: header valid, size: %ld\n",FIB->fib_Size));
            return TRUE;
	}

	return FALSE;
}

BOOL XpkObjExamine( glb glob, struct XpkLock *lock, struct FileInfoBlock *fib ){
	BOOL err;
	struct FileHandle *xfh;
	
	debug(("XpkObjExamine(): "));
	err = xExamine(glob, lock->cfslock.xlock, fib);
	debug(("%ld,'%s'\n",err,fib->fib_FileName));
	if( !err ) return err;

	if( XpkFastModifyFIB( fib ) ) return DOSTRUE;

	xfh = xOpenFromCopyOfLock( glob, lock->cfslock.xlock );
	if( !xfh ){
		debug(("Error: XpkExamine(): Cannot open file from lock %lx.\n",lock));
		return DOSFALSE;
	}
	err = XpkModifyFIB( glob, lock, fib, xfh );
	if( !err ){
		LONG saveioerr;

		saveioerr = glob->ioerr;
		xClose( glob, xfh );
		glob->ioerr = saveioerr;
		return DOSFALSE;
	}else{
		xClose( glob, xfh );
		return DOSTRUE;
	}
}


BOOL XpkObjExamineFH( glb glob, struct XpkFH *fh, struct FileInfoBlock *fib ){
	BOOL err;
	
	debug(("XpkObjExamineFH(): "));
	err = xExamineFH(glob, fh->cfsfh.xfh, fib);
	debug(("%ld,'%s'\n",err,fib->fib_FileName));
	if( !err ) return err;

	if( (strncmp( fib->fib_Comment, XFH_ID, 5 ) == 0) &&
            (strlen( fib->fib_Comment ) == 50) ) fib->fib_Comment[0] = '\0';
        fib->fib_Size = fh->filelen;

	return DOSTRUE;
}


/* XpkModifyFIB() has to fix the length of the file. */
BOOL XpkModifyFIB( glb glob, struct XpkLock *lock,struct FileInfoBlock *fib, struct FileHandle *xfh ){
   struct XpkFib xpkfib;
   
   if(!XpkExamine_FH(glob, xfh, &xpkfib) )
   {
      debug(("Error: IsXpkFile: XpkExamine_FH returned error\n"));
      return FALSE;
   }
   fib->fib_Size = xpkfib.xf_ULen;
   /* ToDo: Should NOT have fixed block size (look at UFS). */
   fib->fib_NumBlocks = (fib->fib_Size+BLOCKSIZE-1) / BLOCKSIZE;

   return DOSTRUE;
}


/*------------------------------------------------------------------------*
 * Initialise and cleanup functions.
 */

BOOL InitXpk( glb glob ){
  
  if(!(glob->XpkBase = OpenLibrary("xpkmaster.library",0L)))
      return FALSE;
   XpkBase = glob->XpkBase;
   if( ((struct Library *)glob->DOSBase)->lib_Version >= 36){
      glob->xpkport = NULL;
   }else{
      if(!(glob->xpkport=CreatePort("CFS port for KS1.3 Xpk-calls.",0L))){
         CloseLibrary(glob->XpkBase);
         debug(("Error creating xpk message port.\n"));
         return FALSE;
      }
   }

   return TRUE;
}


void CleanupXpk( glb glob ){
   
   if( glob->xpkport ) DeletePort( glob->xpkport );
   if( glob->XpkBase ) CloseLibrary( XpkBase );
}

/* xScan support */

static char *ULTA(char *Ptr,ULONG LW)

{
 UWORD Index;

 *Ptr++=' ';
 for (Index=0; Index<8; Index++, LW>>=4) Ptr[7-Index]='A'+(char)(LW&15);
 return &Ptr[8];
}

void xScan(glb glob,struct FileLock *ParentLock,char *Name)

{
 struct FileLock *xScanLock;
 struct FileHandle *xScanHandle;
 struct XpkFib XpkFib;

 if (!glob->xscan) return;

 debug (("xScan(%lx,%s)\n",ParentLock,Name));

 if (xScanLock=xLock(glob,ParentLock,Name,ACCESS_READ))
  {
   if (xExamine(glob,xScanLock,&glob->fib2))
    if (xScanHandle=xOpen(glob,ParentLock,Name,MODE_OLDFILE))
     {
      if (XpkExamine_FH(glob,xScanHandle,&XpkFib))
       if ((glob->fib2.fib_Comment[0]=='\0')||
           (strncmp(glob->fib2.fib_Comment,XFH_ID,strlen(XFH_ID))==0))
        {
         char *Ptr;

         Ptr=strcpy(glob->fib2.fib_Comment,XFH_ID)+strlen(XFH_ID);
         Ptr=ULTA(Ptr,glob->fib2.fib_Date.ds_Days);
         Ptr=ULTA(Ptr,glob->fib2.fib_Date.ds_Minute);
         Ptr=ULTA(Ptr,glob->fib2.fib_Date.ds_Tick);
         Ptr=ULTA(Ptr,glob->fib2.fib_Size);
         *ULTA(Ptr,(XpkFib.xf_Type==XPKTYPE_PACKED)?XpkFib.xf_ULen:glob->fib2.fib_Size)='\0';

         if (!xSetComment(glob,ParentLock,Name,glob->fib2.fib_Comment))
          debug (("failed to set new comment: %ld\n",glob->ioerr));
        }
       else debug (("Comment in use -- nothing done\n"));
      else debug (("failed to XpkExamine\n"));

      (void)xClose(glob,xScanHandle);
     }
    else debug (("failed to open file\n"));
   else debug (("failed to examine\n"));

   (void)xUnLock(glob,xScanLock);
  }
 else debug (("failed to open file\n"));
}

/* End of xpk.c */
