/* dosfunc.c - packet implementations of standard dos functions
   with variations to suit the XFH filing system.
   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 <exec/ports.h>
#include <string.h>
#include "dossupport.h"



/* Lock a file. Passed a parent-lock, a file name, and a mode. */

struct FileLock *xLock(glb glob, struct FileLock *parent,
                       char *name, LONG mode){
   struct MsgPort *procid;
   LONG res1;
   
   procid = parent ? parent->fl_Task : glob->xprocid;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_LOCATE_OBJECT, 3,
                 c2b(parent), cstr2b(name,glob->stringbuf), mode );
   return (struct FileLock *) b2c( res1 );
}


/* Test whether a given file/dir exists. ASSUMES a Lock() on a non-existing
 * object will return ERROR_OBJECT_NOT_FOUND (so if this returns FALSE, one
 * can be pretty sure that the file does not exist).
 * Ceveat: because of race conditions, the returned value cannot be
 * guaranteed to be correct.
 */
BOOL xExists1(glb glob, struct FileLock *parent, char *name){
   struct FileLock *tmplock = xLock(glob, parent, name, ACCESS_READ);
   if(tmplock){
      xUnLock(glob, tmplock);
      return TRUE;
   }else{
      return (BOOL) ((glob->ioerr == ERROR_OBJECT_NOT_FOUND) ? FALSE : TRUE);
   }
}


BOOL xExamine(glb glob, struct FileLock *lock, struct FileInfoBlock *fib){
   struct MsgPort *procid;
   LONG res1;
   
   procid = lock ? lock->fl_Task : glob->xprocid;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_EXAMINE_OBJECT, 2,
                 c2b(lock), c2b(fib) );
                 
   /* Convert bcpl string to c-strings. */
   bstr2cinplace( &fib->fib_FileName[0] );
   bstr2cinplace( &fib->fib_Comment[0] );
   
   return (BOOL) res1;
}


BOOL xExamineNext(glb glob, struct FileLock *lock, struct FileInfoBlock *fib){
   struct MsgPort *procid;
   LONG res1;
   
   /* Convert c-strings to bcpl strings.
    * According to specs, this is not nessesary for the comment. However,
    * 'better safe that sorry'.
    * However, care must be taken to handle the case where the comment is
    * not a valid BSTR. For example, dos.library messes it up in Examine()
    * and ExNext() (converting it to a CSTR and not converting back).
    */
   cstr2binplace(&fib->fib_FileName[0]);
   safecstr2binplace(&fib->fib_Comment[0], 80);
   
   procid = lock ? lock->fl_Task : glob->xprocid;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_EXAMINE_NEXT, 2,
                 c2b(lock), c2b(fib) );
                 
   /* Convert bcpl string to c-strings. */
   bstr2cinplace( &fib->fib_FileName[0] );
   bstr2cinplace( &fib->fib_Comment[0] );
   
   return (BOOL) res1;
}


BOOL xExamineFH(glb glob, struct FileHandle *fh, struct FileInfoBlock *fib){
   LONG res1;
   
   res1 = dopkt( &glob->iopkt, fh->fh_Type, glob->ioport, &glob->ioerr, 
                 ACTION_EXAMINE_FH, 2,
                 fh->fh_Arg1, c2b(fib) );
                 
   /* Convert bcpl string to c-strings. */
   bstr2cinplace( &fib->fib_FileName[0] );
   bstr2cinplace( &fib->fib_Comment[0] );
   
   return (BOOL) res1;
}


struct FileLock *xCreateDir(glb glob, struct FileLock *parent, char *name){
   struct MsgPort *procid;
   LONG res1;
   
   procid = parent ? parent->fl_Task : glob->xprocid;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_CREATE_DIR, 2,
                 c2b(parent), cstr2b(name,glob->stringbuf) );
   return (struct FileLock *) b2c( res1 );
}


BOOL xDeleteFile(glb glob, struct FileLock *parent, char *name){
   struct MsgPort *procid;
   BOOL res1;
   
   procid = parent ? parent->fl_Task : glob->xprocid;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_DELETE_OBJECT, 2,
                 c2b(parent), cstr2b(name,glob->stringbuf) );
   return res1;
}


BOOL xRename(glb glob, struct FileLock *parent1, char *name1,
                       struct FileLock *parent2, char *name2){
   struct MsgPort *procid1, *procid2;
   BOOL res1;
   
   procid1 = parent1 ? parent1->fl_Task : glob->xprocid;
   procid2 = parent2 ? parent2->fl_Task : glob->xprocid;
	if( procid1 != procid2 ){
		debug(("ERROR: xRename() across devices.\n"));
		glob->ioerr = ERROR_RENAME_ACROSS_DEVICES;
		return DOSFALSE;
	}
   res1 = dopkt( &glob->iopkt, procid1, glob->ioport, &glob->ioerr, 
                 ACTION_RENAME_OBJECT, 4,
                 c2b(parent1), cstr2b(name1,glob->stringbuf),
                 c2b(parent2), cstr2b(name2,glob->stringbuf2) );
   return res1;
}


struct FileLock *xParentDir(glb glob, struct FileLock *lock){
   struct MsgPort *procid;
   LONG res1;
   
   procid = lock ? lock->fl_Task : glob->xprocid;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_PARENT, 1,
                 c2b(lock) );
   return (struct FileLock *) b2c( res1 );
}


struct FileLock *xParentFH(glb glob, struct FileHandle *fh){
   LONG res1;

   if ( !fh ) return NULL;

   res1 = dopkt( &glob->iopkt, fh->fh_Type, glob->ioport, &glob->ioerr, 
                 ACTION_PARENT_FH, 1,
                 fh->fh_Args );
   return (struct FileLock *) b2c( res1 );
}


BOOL xSetProtection(glb glob, struct FileLock *parent, char *name, LONG bits){
   struct MsgPort *procid;
   BOOL res1;
   
   procid = parent ? parent->fl_Task : glob->xprocid;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_SET_PROTECT, 4,
                 0L, c2b(parent), cstr2b(name,glob->stringbuf), bits );
   return res1;
}


BOOL xSetComment(glb glob, struct FileLock *parent, char *name, char *comment){
   struct MsgPort *procid;
   BOOL res1;
   
   procid = parent ? parent->fl_Task : glob->xprocid;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_SET_COMMENT, 4,
                 0L, c2b(parent), cstr2b(name,glob->stringbuf),
					               cstr2b(comment,glob->stringbuf2) );
   return res1;
}


BOOL xSetFileDate(glb glob, struct FileLock *parent, char *name, struct DateStamp *ds){
   struct MsgPort *procid;
   BOOL res1;
   
   procid = parent ? parent->fl_Task : glob->xprocid;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_SET_DATE, 4,
                 0L, c2b(parent), cstr2b(name,glob->stringbuf), ds );
   return res1;
}


struct FileLock *xDupLock(glb glob, struct FileLock *lock){
   struct MsgPort *procid;
   LONG res1;
   
   procid = lock ? lock->fl_Task : glob->xprocid;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_COPY_DIR, 1,
                 c2b(lock) );
   return (struct FileLock *) b2c( res1 );
}


BOOL xUnLock(glb glob, struct FileLock *lock){
   struct MsgPort *procid;
   LONG res1;
   
   procid = lock ? lock->fl_Task : glob->xprocid;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_FREE_LOCK, 1,
                 c2b(lock) );
   return (BOOL) res1;
}


/* NOTE: Do NOT call xOpen() with a bogus mode!!! */
struct FileHandle *xOpen(glb glob, struct FileLock *parent,
                       char *name, LONG mode){
   struct MsgPort *procid;
   LONG res1;
   struct FileHandle *fh;
   
   procid = parent ? parent->fl_Task : glob->xprocid;
   if( !dalloc(fh) ){
      OUTOFMEM;
      return NULL;
   }
   fh->fh_Pos=fh->fh_End=-1L;
   fh->fh_Type=procid;
   
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 mode, 3,
                 c2b(fh), c2b(parent), cstr2b(name,glob->stringbuf) );
   if( res1 ){
      return fh;
   }else{
      dfree( fh );
      return NULL;
   }
}


struct FileHandle *xOpenFromLock( glb glob, struct FileLock *lock ){
   struct MsgPort *procid;
   LONG res1;
   struct FileHandle *fh;
   
   procid = lock ? lock->fl_Task : glob->xprocid;
   if( !dalloc(fh) ){
      OUTOFMEM;
      return NULL;
   }
   fh->fh_Pos=fh->fh_End=-1L;
   fh->fh_Type=procid;
   
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_FH_FROM_LOCK, 2,
                 c2b(fh), c2b(lock) );
   if( res1 ){
      return fh;
   }else{
      dfree( fh );
		/* Attemp to open file with Open(lock,""). */
		if( fh = xOpen( glob, lock, "", MODE_OLDFILE ) ){
			xUnLock( glob, lock );
			return fh;
		}else{
			struct FileLock *parent;
			struct FileInfoBlock *fib;
			
			/* Attemp to open file with Open(ParentDir(lock),name). */
			if( !dalloc(fib) ){
				OUTOFMEM;
				return NULL;
			}
			if( !xExamine( glob, lock, fib ) ){
				dfree(fib);
				return NULL;
			}
			if( !(parent = xParentDir( glob, lock )) ){
				dfree(fib);
				return NULL;
			}
			if( fh = xOpen( glob, parent, fib->fib_FileName, MODE_OLDFILE )){
				xUnLock( glob, parent );
				xUnLock( glob, lock );
				dfree(fib);
				return fh;
			}
			xUnLock( glob, parent );
			dfree(fib);
			return NULL;
		}
	}
}

struct FileHandle *xOpenFromCopyOfLock(glb glob,struct FileLock *FileLock)

{
 struct FileLock *ParLock;
 struct FileHandle *FH;

 debug (("xOpenFromCopyOfLock(): Envoy Kludge is %s\n",glob->EnvoyKludge?"ON":"OFF"));
 if (glob->EnvoyKludge)
  {	
   if ((ParLock=xParentDir(glob,FileLock))==NULL) return NULL;

   if (xExamine(glob,FileLock,&glob->fib2))
    FH=xOpen(glob,ParLock,glob->fib2.fib_FileName,
             (FileLock->fl_Access==ACCESS_WRITE)?MODE_READWRITE:MODE_OLDFILE);

   xUnLock (glob,ParLock);
   return FH;
  }
 else
  {
   if ((ParLock=xDupLock(glob,FileLock))==NULL) return NULL;

   if ((FH=xOpenFromLock(glob,ParLock))==NULL)
    {
     xUnLock (glob,ParLock);
     return NULL;
    }
   return FH;
  }
}

BOOL xClose(glb glob, struct FileHandle *fh){
   struct MsgPort *procid;
   LONG res1;
   
   if( !fh ) return DOSTRUE;
   procid = fh->fh_Type;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_END, 1,
                 fh->fh_Arg1 );
   if( res1 ) dfree( fh );
   return (BOOL) res1;
}


LONG xRead(glb glob, struct FileHandle *fh, void *buf, LONG len){
   struct MsgPort *procid;
   LONG res1;
   
   if( !fh ) return 0L;
   procid = fh->fh_Type;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_READ, 3,
                 fh->fh_Arg1, (LONG)buf, len );
   return res1;
}


LONG xWrite(glb glob, struct FileHandle *fh, void *buf, LONG len){
   struct MsgPort *procid;
   LONG res1;
   
   if( !fh ) return 0L;
   procid = fh->fh_Type;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_WRITE, 3,
                 fh->fh_Arg1, (LONG)buf, len );
   return res1;
}


LONG xSeek(glb glob, struct FileHandle *fh, LONG pos, LONG offset){
   struct MsgPort *procid;
   LONG res1;
   
   if( !fh ) return -1L;
   procid = fh->fh_Type;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_SEEK, 3,
                 fh->fh_Arg1, pos, offset );
   return res1;
}


LONG xSetFileSize(glb glob, struct FileHandle *fh, LONG pos, LONG offset){
   struct MsgPort *procid;
   LONG res1;
   
   if( !fh ) return -1L;
   procid = fh->fh_Type;
   /* ToDo: NOTE: The docs say that this call takes the *file handle* in
    * Arg1 (and not the usual fh->fh_arg1). This should be tested! */
   debug(("xSetFileSize: Sending packet..."));
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_SET_FILE_SIZE, 3,
                 c2b(fh), pos, offset );
   debug(("returned! with %ld (ioerr=%ld).\n",res1,glob->ioerr));
   return res1;
}


BOOL xChangeMode(glb glob, ULONG type, void *obj, ULONG mode){
  struct MsgPort *procid;
  BOOL res1;

  if(!obj) return FALSE;
  switch(type){
  case CHANGE_FH:
    procid = ((struct FileHandle *)obj)->fh_Type;
    break;
  case CHANGE_LOCK:
    procid = ((struct FileLock *)obj)->fl_Task;
    break;
  default:
    debug(("Error: xChangeMode(): Bad type value %ld.\n",type));
    glob->ioerr = ERROR_OBJECT_WRONG_TYPE;
    return FALSE;
  }
  res1 = dopkt(&glob->iopkt, procid, glob->ioport, &glob->ioerr, 
	       ACTION_CHANGE_MODE, 3,
	       type, c2b(obj), mode);
  return res1;
}


BOOL xInfo(glb glob, struct FileLock *lock, struct InfoData *info){
   struct MsgPort *procid;
   LONG res1;
   
   if( !info ) return DOSFALSE;
   if( !lock ) lock = glob->xrootlock;
   procid = lock->fl_Task;
   res1 = dopkt( &glob->iopkt, procid, glob->ioport, &glob->ioerr, 
                 ACTION_INFO, 2,
                 c2b(lock), c2b(info) );
   return (BOOL) res1;
}


BOOL xMakeLink(glb glob,struct FileLock *FL,char *Name,LONG Dest,LONG Type)

{
 if (FL==NULL) FL=glob->xrootlock;
 return (BOOL)dopkt(&glob->iopkt,FL->fl_Task,glob->ioport,&glob->ioerr,
                    ACTION_MAKE_LINK,4,
                    c2b(FL),cstr2b(Name,glob->stringbuf),
                    Dest,Type);
}

BOOL xReadLink(glb glob,struct FileLock *FL,char *Name,char *Buffer,ULONG Size)

{
 if (FL==NULL) FL=glob->xrootlock;
 return (BOOL)dopkt(&glob->iopkt,FL->fl_Task,glob->ioport,&glob->ioerr,
                    ACTION_READ_LINK,4,
                    c2b(FL),Name,Buffer,Size);
}

/* ToDo: Must search for doc. on correct behavior in case of a NULL lock. */
/* For now, a null lock is different from a lock to the root dir. */
LONG xSameLock( glb glob, struct FileLock *l1, struct FileLock *l2 ){
   BOOL res1;
   struct MsgPort *prcid1, *prcid2;
   BPTR vol1,vol2;
   
/* debug(("xSameLock(%lx,%lx) ",l1,l2));    */
   prcid1 = l1 ? l1->fl_Task : glob->xprocid;
   prcid2 = l2 ? l2->fl_Task : glob->xprocid;
   if( !l1 || !l2 )     /* Test for a NULL lock. */
      return l1==l2 ? LOCK_SAME :
                      prcid1==prcid2 ? LOCK_SAME_HANDLER : LOCK_DIFFERENT;
   vol1 = l1->fl_Volume;
   vol2 = l2->fl_Volume;
   if( vol1 != vol2 || prcid1 != prcid2 ) return LOCK_DIFFERENT;
   res1 = dopkt( &glob->iopkt, prcid1, glob->ioport, &glob->ioerr, 
                 ACTION_SAME_LOCK, 2,
                 c2b(l1), c2b(l2) );
   if( !res1 && glob->ioerr == ERROR_ACTION_NOT_KNOWN ){
      /* Klugde to support pre-2.0 file systems that cannot handle the
       * SameLock() function. To simulate it, the full paths for both locks
       * are obtained and compared.
       */
      if( !xgetpath( glob, l1, &glob->stringbuf[0], MAXSTRING-1 ) )
         res1 = FALSE;
      else if( !xgetpath( glob, l2, &glob->stringbuf2[0], MAXSTRING-1 ) )
         res1 = FALSE;
      else{
         res1 = !strcmp(&glob->stringbuf[0],&glob->stringbuf2[0]);
         debug(("xSameLock(): '%s' '%s' -> %ld\n",
                &glob->stringbuf[0],&glob->stringbuf2[0],res1));
      }
   }
/* debug((": result %ld\n",res1)); */
   return res1 ? LOCK_SAME : LOCK_SAME_HANDLER;
}


/* A strncat() that allows zero or negative max length. */
static char *mystrncat(char *d,char *s,int num){
   if(num>0) return strncat(d,s,num);
   else return d;
}


/* The recursion part of the xgetpath() function (see below). */
static BOOL xgetpath1(glb glob, struct FileLock *Lock,char *f,
                     struct FileInfoBlock *finfo,int max){
   char fname[31];
   LONG type;
   struct FileLock *parent;
   BOOL result;
   
   if( !xExamine( glob, Lock, finfo) ) return FALSE;
   fname[30]='\0';
   strncpy(fname,finfo->fib_FileName,30);
   type = finfo->fib_DirEntryType;
   if( parent = xParentDir(glob, Lock) ){
      result = xgetpath1( glob, parent, f, finfo, max );
      xUnLock( glob, parent );
      if(!result) return result;
   }
   mystrncat( f, fname, max-strlen(f) );
   if(type>0) mystrncat( f, parent ? "/" : ":",max-strlen(f) );
   return TRUE;
}


/* Get the full path name of an xlock in the buffer 'f' with lenght max.
 * Returns TRUE if succesfull, FALSE if an error occured.
 */
BOOL xgetpath(glb glob, struct FileLock *Lock, char *f, int max){
   struct FileInfoBlock *finfo;
   BOOL err;
   
   if( !dalloc(finfo) ) return FALSE;
   strcpy(f,"");
   err = xgetpath1( glob, Lock, f, finfo, max);
   dfree(finfo);
   return err;
}


/* Generation of temporary file names. 
 * Called with a buffer of at least TMPNAMEMAXSIZE bytes.
 */
static void NewTmpFile(glb glob, char *name){
   SPrintF(name,TMPNAMETEMPLATE,glob->mytask,glob->tmpfilecount++);
}


/* Clone protection flags etc. of file (or dir). */
BOOL CloneFile2File(glb glob, struct FileLock *parent1, char *name1,
		              struct FileLock *parent2, char *name2){
  struct FileLock *lock;

  if(!(lock = xLock(glob, parent1, name1, ACCESS_READ))){
    debug(("Error: CloneFile2File(): cannot lock file: %ld.\n",glob->ioerr));
    return FALSE;
  }
  if(!xExamine(glob, lock, &glob->fib2)){
    SAVEIOERR;
    debug(("Error: CloneFile2File(): couldn't xExamine(): %ld.\n",glob->ioerr));
    xUnLock(glob, lock);
    RESTIOERR;
    return FALSE;
  }
  xUnLock(glob, lock);
  if(!xSetFileDate(glob, parent2, name2, &glob->fib2.fib_Date)){
    debug(("Error: CloneFile2File(): xSetFileDate / %ld.\n",glob->ioerr));
    return FALSE;
  }
  if(!xSetComment(glob, parent2, name2, &glob->fib2.fib_Comment[0])){
    debug(("Error: CloneFile2File(): xSetComment / %ld.\n",glob->ioerr));
    return FALSE;
  }
  if(!xSetProtection(glob, parent2, name2, glob->fib2.fib_Protection)){
    debug(("Error: CloneFile2File(): xSetProtection / %ld.\n",glob->ioerr));
    return FALSE;
  }
  return TRUE;
}


/* Transforming a file handle (see TranformFile(), below for details).
 * NOTE BIEN: this function will close the file handle.
 */
BOOL TransformXFH(glb glob, struct FileHandle *srcfh,
     struct FileLock *parent, char *name,
     BOOL (*f)(glb, struct FileHandle *, struct FileHandle *, void *),
     void *userdata){
  char tmp1[TMPNAMEMAXSIZE], tmp2[TMPNAMEMAXSIZE];
  struct FileHandle *dstfh;
  BOOL res;
  DECLIOERR;
  
  NewTmpFile(glob, tmp1);
  if(!(dstfh = xOpen(glob, parent, tmp1, MODE_NEWFILE))){
    debug(("Error: TransFormFile: Cannot open destination '%s': %ld.\n",tmp1,glob->ioerr));
    PUTIOERR;
    goto cleanup_src;
  }
  /* Now do the magic stuff. */
  res = (*f)(glob, srcfh, dstfh, userdata);
  if(!res){
    debug(("Error: TransFormFile: User func returned error: %ld.\n",glob->ioerr));
    PUTIOERR;
    goto cleanup_src_dst;
  }
  if(!xClose(glob, dstfh)){
    /* We can't close the new file - play it safe and keep the old
     * file as it is. */
    debug(("Error: TransFormFile: Error closing new file: %ld.\n",glob->ioerr));
    PUTIOERR;
    goto cleanup_delete_dst;
  }
  /* If we can't close the old file, we'll just have to hope for the 
   * best... (it's a read handle, after all). */
  xClose(glob, srcfh);
  
  /* Now copy the protection flags etc. from the source file. */
  if(!CloneFile2File(glob, parent, name, parent, tmp1)){
    debug(("Error: TransFormFile(): Error cloning file: %ld.\n",glob->ioerr));
    goto failexit;
  }
  
  /* Rename the original file (to make room for the new one). */
  NewTmpFile(glob, tmp2);
  if(!xRename(glob, parent, name, parent, tmp2)){
    /* Something went wrong moving the old file. Better carry on,
     * however, in case the file did move (if it did not we'll fail
     * trying to replace it with the new one).
     */
    debug(("Error: TransFormFile: Couldn't rename the old file: %ld.\n",glob->ioerr));
  }
  if(!xRename(glob, parent, tmp1, parent, name)){
    /* Here comes trouble... we've got the old file away, and the new
     * one won't fall in place. We'll just try to save as much as
     * possible. */
    PUTIOERR;
    if(xRename(glob, parent, tmp2, parent, name)){
      /* We only delete the new file if we are "sure" that the old one
       * is OK. We might end up with two files this way, but it's safer.
       */
      xDeleteFile(glob, parent, tmp1);
    }
    RESTIOERR;
    return FALSE;
  }
  
  /* Great! The new file is well and in place. Let's delete the old
   * one and return (we return succes even if the old file wouldn't go
   * away. */
  xDeleteFile(glob, parent, tmp2);
  return TRUE;
  /*-----------------------------------------------------------------------*/
  
  /* Common clean-up code in error case. Remember to set RESTIOERR. */
 cleanup_src_dst:
  /* Now clean up the mess. Any of these calls may fail; we'll just
   * have to make the best of it. */
  
  /* We try to truncate the file to save flushing buffers. */
  /* This call is switchable, since the docs are dubious... */
  if(glob->truncateonpack){
    xSetFileSize(glob, dstfh, 0L, OFFSET_BEGINNING);
  }else{
    debug(("File truncation not requested... skipping.\n"));
  }
  xClose(glob, dstfh);
 cleanup_delete_dst:
  xDeleteFile(glob, parent, tmp1);
 cleanup_src:
  xClose(glob, srcfh);    /* Leave the file as it was. */
  RESTIOERR;
 failexit:
  return FALSE;
}


/* The main function for transforming files (typically for packing a
 * file after ACTION_END). Arguments 'parent' and 'name' refer to the
 * file to be transformed. Additionally, a function and data pointer
 * is passed. The function will receive source and destination file
 * handles for it to do its magic on.
 */
BOOL TransformFile(glb glob, struct FileLock *parent, char *name,
     BOOL (*f)(glb, struct FileHandle *, struct FileHandle *, void *),
     void *userdata){
  struct FileHandle *srcfh;
  
  if(!(srcfh = xOpen(glob, parent, name, MODE_OLDFILE))){
    debug(("Error: TransFormFile: Cannot open source: %ld.\n",glob->ioerr));
    return FALSE;
  }
  return TransformXFH(glob, srcfh, parent, name, f, userdata);
}


/* Get the size of an open file. Returns size or -1L if error.
 * Currently, in case of error, the file position may change.
 */
LONG xGetFileSize(glb glob, struct FileHandle *fh){
  LONG oldpos,size;
  
  /* Todo: Should first try Examine_FH() (and perhaps even try
   * SetFileSize(..,0,OFFSET_END) ). */
  oldpos = xSeek(glob, fh, 0L, OFFSET_END);
  if(oldpos==-1L) return oldpos;
  size = xSeek(glob, fh, oldpos, OFFSET_BEGINNING);
  return size;
}


/* Write string to output file, returning success / failure. */
BOOL xWriteStr(glb glob, struct FileHandle *fh, char *str){
  LONG len = strlen(str);

  if(len != xWrite(glob, fh, str, len)){
    debug(("Error: xWriteStr(): xWrite(): %ld.\n",glob->ioerr));
    return FALSE;
  }
  return TRUE;
}


/* End of dosfunc.h */
