/* file.c - file-handle stuff for the XFH file system handler.
   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 <string.h>

#include "dossupport.h"


/*
 * RawCFSOpen() is the low-lewel Open() function of the CFS file
 * system. It takes a simple filename (that is, no path specification),
 * a lock to the parent directory, and a 'mode' value as arguments. A
 * special case occurs for a null name, in which case it opens the file
 * associated with the passed lock.
 */

struct CFSFH *RawCFSOpen(glb glob, struct CFSLock *lock,
                        char * name, LONG mode ){
   struct FileHandle *xfh;
   void *fh;

   /* Here is the place to check whether the lock refers to a
    * directory in the underlying file system, or if it is in fact
    * a directory in an archieve. This code is for the former case: */

   if( lock->objtype == XOBJECT ){
      LONG type;
      BOOL isnew = FALSE;   /* Whether MODE_READWRITE file is new. */

      /* ToDo: Need better way to determine the correct action. */
      if( mode != MODE_OLDFILE && mode != MODE_NEWFILE && mode != MODE_READWRITE ){
         debug(("Error: RawCFSOpen(): Unsupported Open() mode %ld\n",mode));
         glob->ioerr = ERROR_ACTION_NOT_KNOWN;
         return NULL;
      }

      if(mode==MODE_READWRITE){
         /* Check that append mode is allowed. */
         if(!glob->allowappend){
            debug(("Append mode not set... failing.\n"));
            glob->ioerr = ERROR_ACTION_NOT_KNOWN;
            return NULL;
         }
         /* Try to determine whether file already exists. NOTE: this is
          * NOT fool-proof, since there is a (slight) change that the
          * file may suddenly appear just before the xOpen() call.
          */
         isnew = !xExists1(glob, lock->xlock, name);
      }
      if( !(xfh=xOpen(glob, lock->xlock, name, mode)) ){
         debug(("RawCFSOpen(): Object %s could not be opened: %d.\n",name,glob->ioerr));
         return NULL;
      }
      if( mode == MODE_NEWFILE ){
         /* NOTE: the name cannot be "" (since then it's already in use). */
         if(!strcmp(name,"")){
            debug(("Strange...empty name in open new file - failing.\n"));
            xClose(glob, xfh);
            xDeleteFile(glob, lock->xlock, name);
            glob->ioerr = ERROR_OBJECT_IN_USE;
            return NULL;
         }
         if(!(fh = XObjCreateFH(glob, xfh, mode, name, lock))){
            SAVEIOERR;
            xClose(glob, xfh);
            xDeleteFile(glob, lock->xlock, name);
            RESTIOERR;
            return NULL;
         }
      }else{   /* MODE_OLDFILE or MODE_READWRITE */
         type = xFileType( glob, xfh );
         /* 'type' will be XObj for new file created with MODE_READWRITE. */
         if( type == XPKOBJECT ){
            if( mode==MODE_OLDFILE ){
               struct CFSFH *newfh=(struct CFSFH *)XpkOpenOldFile( glob, xfh );
               if(!newfh){
                  SAVEIOERR;
                  xClose(glob,xfh);
                  RESTIOERR;
                  return NULL;
               }
	       /* Ready for Write() if needed. */
	       if(!XObjStuffFH(glob, newfh, name, lock)){
		 SAVEIOERR;
		 Xpk_Close(glob, (struct XpkFH *)newfh);
		 RESTIOERR;
		 return NULL;
	       }
               fh = newfh;
            }else{     /* MODE_READWRITE */
               BOOL res;
               
               xClose(glob,xfh);
               debug(("Attempting to uncompress file %s.\n",name));
               res = TransformFile(glob, lock->xlock, name, UnPackFile2File, NULL);
               debug(("TransformFile() returned: %ld.\n",res));
               if(!res) return NULL;
               if( !(xfh=xOpen(glob, lock->xlock, name, mode)) ){
                  debug(("Error: File %s could not be re-opened: %d.\n",name,glob->ioerr));
                  return NULL;
               }
               fh = XObjCreateFH(glob, xfh, mode, name, lock);
               if(!fh){
                  SAVEIOERR;
                  xClose(glob, xfh);
                  RESTIOERR;
                  return NULL;
               }
            }
         }else{
            /* A plain file. If the mode is MODE_READWRITE and the file
             * was newly created, the user can request the file to be
             * autocompressed. Otherwise, the file will not be compressed.
             */
            if(mode==MODE_READWRITE && isnew && glob->compressreadwrite){
               fh = XObjCreateFH(glob, xfh, mode, name, lock);
            }else{
               fh = XObjCreateFH(glob, xfh, mode, NULL, NULL);
            }
            if(!fh){
               SAVEIOERR;
               xClose(glob, xfh);
               RESTIOERR;
               return NULL;
            }
         }
      }
   }else if( lock->objtype == XPKOBJECT ){  /* Allow Open(xpklock,""). */
      if( mode != MODE_OLDFILE ){
         debug(("Bad open from XpkLock (mode=%ld).\n",mode));
         if(!strcmp(name,"")){
            glob->ioerr = ERROR_OBJECT_IN_USE;
         }else{
            glob->ioerr = ERROR_OBJECT_WRONG_TYPE;
         }
         return NULL;
      }else if(strcmp(name,"")){
         debug(("Bad open (non-null filename) from Xpklock.\n"));
         glob->ioerr = ERROR_OBJECT_WRONG_TYPE;
         return NULL;
      }else{
         struct CFSFH *newfh = (struct CFSFH *)
	   XpkOpenOldFileFromCopyOfLock( glob, (struct XpkLock *)lock );
         if(!newfh){
            return NULL;
         }
	 /* Ready for Write() if needed. */
	 if(!XObjStuffFH(glob, newfh, name, lock)){
	   SAVEIOERR;
	   Xpk_Close(glob, (struct XpkFH *)newfh);
	   RESTIOERR;
	   return NULL;
	 }
         fh = newfh;
      }
   }else{
      debug(("** PANIC **: bad object type in lock: %ld\n",lock->objtype));
      glob->ioerr = ERROR_OBJECT_WRONG_TYPE;
      return NULL;
   }
   return fh;
}


struct CFSFH *CFSOpen(glb glob,struct CFSLock * parentlock,
                          char * name,LONG mode){
   struct CFSLock *parent;
   struct CFSFH *fh;
   LONG saveioerr;
   
   /* Lock the parent directory, and set 'name' to point to the actual
    * name. For a path like 'name:', lock the file/dir and point name to
    * "".
    */
   if(!(parent = CFSLockParent(glob, parentlock, &name ))){
      debug(("Error: CFSOpen(): Could not CFSLockParent(): %ld.\n",glob->ioerr));
      return NULL;
   }
   
   /* Now Lock the file or dir. */
   fh = RawCFSOpen( glob, parent, name, mode );
   saveioerr = glob->ioerr;
   CFSUnLock( glob, parent );
   glob->ioerr = saveioerr;
   
   return fh;
}


BOOL CFSClose( glb glob, struct CFSFH *fh ){

   if( !fh ) return DOSTRUE;
   return (BOOL) (*fh->f->Close)( glob, fh );
}


LONG CFSRead( glb glob, struct CFSFH *fh, void *buf, LONG len ){
   
   if( !fh )
      return 0L;
   else
      return (*fh->f->Read)( glob, fh, buf, len );
}


LONG CFSWrite( glb glob, struct CFSFH *fh, void *buf, LONG len ){
   
   if( !fh )
      return 0L;
   else
      return (*fh->f->Write)( glob, fh, buf, len );
}


LONG CFSSeek( glb glob, struct CFSFH *fh, LONG pos, LONG offset ){
   
   if( !fh )
      return -1L;
   else if( !fh->f ){
      debug(("** PANIC **: CFSSeek(): NULL action func.\n"));
      glob->ioerr = ERROR_OBJECT_WRONG_TYPE;
      return -1L;
   }else
      return (*fh->f->Seek)( glob, fh, pos, offset );
}

/* End of file.c */
