/* Copyright 1995 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

/* NOTE:  This is the quick hack version that uses old-style calls.
          Eventually we should extract the common functionality and
	  then use that for both.  That will be a little cleaner, a little
	  faster and a little easier to debug, but not enough of any to
	  make it sensible to do that initially.  */


#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_fileHighlevel[] =
	"$Id: fileHighlevel.c 86 2005-05-25 00:47:12Z ctm $";
#endif

#include "rsys/common.h"

#include "MemoryMgr.h"
#include "FileMgr.h"
#include "ProcessMgr.h"

#include "rsys/file.h"
#include "rsys/string.h"
#include "rsys/hfs.h"
#include "rsys/executor.h"

/*
 * extract the last component of a name:
 *
 * aaa:bbb:ccc  -> ccc
 * aaa:bbb:ccc: -> ccc
 */

PRIVATE void
extract_name (Str255 dest, StringPtr source)
{
  int len, new_len;
  Byte *p;
  
  len = source[0];
  if (source[len] == ':') /* ignore trailing ':' */
    --len;
  for (p = source + len; p >= source+1 && *p != ':'; --p)
    ;
  new_len = len - (p - source);
  dest[0] = new_len;
  memmove (dest+1, p+1, new_len);
}

P4 (PUBLIC pascal trap, OSErr, FSMakeFSSpec,
    int16, vRefNum, int32, dir_id,
    Str255, file_name, FSSpecPtr, spec)
{
  Str255 local_file_name;
  OSErr retval;
  HParamBlockRec hpb;

  if (!file_name)
    warning_unexpected("!file_name");

/*
 * Colons make things tricky because they are used both to identify volumes
 * and to delimit directories and sometimes to identify partial paths.  Right
 * now, most of these uses of colon shouldn't cause problems, but there are
 * some issues that need to be checked on a real Mac.  What do all these
 * mean?
 *
 *     dir_id      file_name
 *     310         foo:
 *     310         :foo:
 *     310         foo:bar
 *     310         :foo:bar
 *     310         :foo:bar:
 *
 * what about all those file_names, but with dir_id = 0, dir_id = 1, dir_id = 2
 * and dir_id = a number that isn't a directory id?
 */

  if (pstr_index_after (file_name, ':', 0))
    warning_unexpected ("colon found");

  str255assign (local_file_name, file_name);
  hpb.volumeParam.ioNamePtr = (StringPtr) RM ((Ptr) local_file_name);
  hpb.volumeParam.ioVRefNum = CW (vRefNum);
  if (file_name[0])
    hpb.volumeParam.ioVolIndex = CLC (-1);
  else
    hpb.volumeParam.ioVolIndex = CLC (0);

  retval = PBHGetVInfo (&hpb, FALSE);

  if (retval == noErr)
    {
      CInfoPBRec cpb;
      
      str255assign (local_file_name, file_name);
      cpb.hFileInfo.ioNamePtr = (StringPtr) RM ((Ptr) local_file_name);
      cpb.hFileInfo.ioVRefNum = CW (vRefNum);
      if (file_name[0])
	cpb.hFileInfo.ioFDirIndex = CWC (0);
      else
	cpb.hFileInfo.ioFDirIndex = CWC (-1);
      cpb.hFileInfo.ioDirID = CL (dir_id);
      retval = PBGetCatInfo (&cpb, FALSE);
      if (retval == noErr)
	{
	  spec->vRefNum = hpb.volumeParam.ioVRefNum;
	  spec->parID = cpb.hFileInfo.ioFlParID;
	  extract_name ((StringPtr) spec->name, MR (cpb.hFileInfo.ioNamePtr));
	}
      else if (retval == fnfErr)
	{
	  OSErr err;
	  
	  cpb.hFileInfo.ioNamePtr = CLC (0);
	  cpb.hFileInfo.ioVRefNum = CW (vRefNum);
	  cpb.hFileInfo.ioFDirIndex = CWC (-1);
	  cpb.hFileInfo.ioDirID = CL (dir_id);
	  err = PBGetCatInfo (&cpb, FALSE);
	  if (err == noErr)
	    {
	      if (cpb.hFileInfo.ioFlAttrib & ATTRIB_ISADIR)
		{
		  spec->vRefNum = hpb.volumeParam.ioVRefNum;
		  spec->parID = CL (dir_id);
		  extract_name ((StringPtr) spec->name, file_name);
		}
	      else
		retval = dupFNErr;
	    }
	}
    }
  return retval;
}

#if 0

typedef struct save_fcb_info_str
{
  filecontrolblock fcb;
  HVCB *vptr;
  struct save_fcb_info_str *next;
  INTEGER refnum; /* zero if this is just info gathered for switching
		     purposes */
}
save_fcb_info_t;

PRIVATE LONGINT
get_file_num (FSSpecPtr fsp)
{
  return 0; /* TODO */
}

/*
 * get_fcb_info squirrels away all the FCB entries that match a particular
 * file.  It then alters them so that we can open files that are already
 * open without trouble.
 */

PRIVATE save_fcb_info_t *
get_fcb_info (FSSpecPtr fsp)
{
  filecontrolblock *fcbp, *efcbp;
  INTEGER total_length, fcb_size;
  save_fcb_info_t *retval;
  char *fcbsptr;
  INTEGER swapped_vrefnum;
  LONGINT swapped_fnum;


  retval = 0;

  swapped_vrefnum = fsp->vRefNum;
  swapped_fnum = CL (get_file_num (fsp));

  fcbsptr = (char *) CL (FCBSPtr);
  total_length = CW(*(short *)fcbsptr);
  fcbp = (filecontrolblock *) ((short *)CL(FCBSPtr)+1);
  efcbp = (filecontrolblock *) ((char *)CL(FCBSPtr) + total_length);
  fcb_size = CW (FSFCBLen);
  for (;fcbp < efcbp; fcbp = (filecontrolblock *) ((char *)fcbp + fcb_size))
    {
      HVCB *vptr;

      vptr = CL (fcbp->fcbVPtr);
      if (vptr && vptr->vcbVRefNum == swapped_vrefnum
	  && fcbp->fcbFlNum == swapped_fnum)
	{
	  save_fcb_info_t *newp;

	  newp = malloc (sizeof *newp);
	  newp->refnum = (char *) fcbp - fcbsptr;
	  newp->fcb = *fcbp;
	  newp->next = retval;
	  newp->vptr = fcbp->fcbVPtr;
	  fcbp->fcbVPtr = 0; /* hide this open file */
	  retval = newp;
	}
    }
  return retval;
}

PRIVATE OSErr
exchange_forks (FSSpecPtr src, FSSpecPtr dst, forktype type)
{
  /* TODO */
  return paramErr;
}

PRIVATE void
exchange_fcb (const save_fcb_info_t *dstp, const save_fcb_info_t *srcp)
{
  /* TODO */
}

/*
 * make_fcb_info looks up the info that we need in order to swap fcb info
 * when we only have information about one side
 */

PRIVATE save_fcb_info_t *
make_fcb_info (FSSpecPtr fsp)
{
  save_fcb_info_t *retval;

  retval = malloc (sizeof *retval);
  retval->next = 0;
  retval->vptr = 0; /* not needed */
  retval->refnum = 0; /* means we're not associated with a real FCB */
  return retval;
}

PRIVATE void
exchange_fcbs (FSSpecPtr fs1, const save_fcb_info_t *savefcbp1,
	       FSSpecPtr fs2, const save_fcb_info_t *savefcbp2)
{
  if (savefcbp1 || savefcbp2)
    {
      if (!savefcbp1)
	savefcbp1 = make_fcb_info (fs1);
      else if (!savefcbp2)
	savefcbp2 = make_fcb_info (fs2);
    }
  exchange_fcb (savefcbp1, savefcbp2);
  exchange_fcb (savefcbp2, savefcbp1);
}

PRIVATE void
restore_fcb (const save_fcb_info_t *infop)
{
  char *fcbsptr;

  fcbsptr = (char *) CL (FCBSPtr);
  if (infop)
    {
      if (infop->refnum)
	{
	  filecontrolblock *fcbp;

	  fcbp = (filecontrolblock *) (fcbsptr + infop->refnum);
	  fcbp->fcbVPtr = infop->vptr;
	}
      restore_fcb (infop->next);
    }
}

PRIVATE void
release_fcb_info (save_fcb_info_t *infop)
{
  if (infop)
    {
      release_fcb_info (infop->next);
      free (infop);
    }
}

#endif

/*
 * Part of ugly PAUP-specific hack below
 */

PRIVATE void
create_temp_name (Str63 name, int i)
{
  OSErr err;
  ProcessSerialNumber psn;

  err = GetCurrentProcess (&psn);
  if (err == noErr)
    sprintf ((char *) name+1,
	     "%x%x.%x", CL (psn.highLongOfPSN), CL (psn.lowLongOfPSN), i);
  else
    sprintf ((char *) name+1, "%d.%x", err, i);
  name[0] = strlen ((char *) name+1);
}

/*
 * On real HFS volumes we could do some B-tree manipulation to achieve
 * the correct results, *but* we'd need this code for ufs volumes anyway,
 * so for now we do the same thing on both.
 */

P2 (PUBLIC pascal trap, OSErr, FSpExchangeFiles,
    FSSpecPtr, src, FSSpecPtr, dst)
{
#if 0
  save_fcb_info_t *src_fcb_info, *dst_fcb_info;
  OSErr retval;

  src_fcb_info = get_fcb_info (src);
  dst_fcb_info = get_fcb_info (dst);

  retval = exchange_forks (src, dst, datafork);
  if (retval == noErr)
    {
      retval = exchange_forks (src, dst, resourcefork);
      if (retval != noErr)
	exchange_forks (src, dst, datafork); /* try to put things back
						together */
    }

  if (retval == noErr)
    exchange_fcbs (src, src_fcb_info, dst, dst_fcb_info);
  else
    {
      restore_fcb (src_fcb_info);
      restore_fcb (dst_fcb_info);
    }

  release_fcb_info (src_fcb_info);
  release_fcb_info (dst_fcb_info);

  return retval;
#else
  OSErr retval;

  warning_unimplemented ("poorly implemented");
  if (src->vRefNum != dst->vRefNum)
    retval = diffVolErr;
  else if (ROMlib_creator != TICK("PAUP") || src->parID != dst->parID)
    retval = wrgVolTypeErr;
  else
    {
      /* Evil hack to get PAUP to work -- doesn't bother adjusting FCBs */
      FSSpec tmp_spec;
      int i;
      
      i = 0;
      tmp_spec = *dst;
      do
	{
	  create_temp_name (tmp_spec.name, i++);
	  retval = FSpRename (dst, tmp_spec.name);
	}
      while (retval == dupFNErr);
      if (retval == noErr)
	{
	  retval = FSpRename (src, dst->name);
	  if (retval != noErr)
	    FSpRename (&tmp_spec, dst->name);
	  else
	    {
	      retval = FSpRename (&tmp_spec, src->name);
	      if (retval != noErr)
		{
		  FSpRename (dst, src->name);
		  FSpRename (&tmp_spec, dst->name);
		}
	    }
	} 
    }
  return retval;
#endif
}

typedef OSErrRET (*open_procp) (HParmBlkPtr pb, BOOLEAN sync);

PRIVATE OSErr
open_helper (FSSpecPtr spec, SignedByte perms, int16 *refoutp,
	     open_procp procp)
{
  OSErr retval;
  HParamBlockRec hpb;

  hpb.ioParam.ioVRefNum = spec->vRefNum;
  hpb.fileParam.ioDirID = spec->parID;
  hpb.ioParam.ioNamePtr = (StringPtr) RM ((Ptr) spec->name);
  if (perms == fsWrPerm)
    {
      warning_unexpected (NULL_STRING);
      perms = fsRdWrPerm;
    }
  hpb.ioParam.ioPermssn = perms;
  warning_unimplemented ("poorly implemented ... will try to open drivers");
  retval = procp (&hpb, FALSE);
  if (retval == noErr)
    *refoutp = hpb.ioParam.ioRefNum;
  return retval;
}

P3 (PUBLIC pascal trap, OSErr, FSpOpenDF,
    FSSpecPtr, spec, SignedByte, perms, int16 *, refoutp)
{
  return open_helper (spec, perms, refoutp, PBHOpen);
}

P3 (PUBLIC pascal trap, OSErr, FSpOpenRF,
    FSSpecPtr, spec, SignedByte, perms, int16 *, refoutp)
{
  return open_helper (spec, perms, refoutp, PBHOpenRF);
}

P4 (PUBLIC pascal trap, OSErr, FSpCreate,
    FSSpecPtr, spec, OSType, creator, OSType, file_type,
    ScriptCode, script)
{
  OSErr retval;

  retval = HCreate (CW (spec->vRefNum), CL (spec->parID), spec->name,
		    creator, file_type);
  return retval;
}

P3 (PUBLIC pascal trap, OSErr, FSpDirCreate,
    FSSpecPtr, spec, ScriptCode, script,
    int32 *, created_dir_id)
{
  OSErr retval;
  HParamBlockRec hpb;

  hpb.ioParam.ioVRefNum = spec->vRefNum;
  hpb.fileParam.ioDirID = spec->parID;
  hpb.ioParam.ioNamePtr = (StringPtr) RM ((Ptr) spec->name);
  retval = PBDirCreate (&hpb, FALSE);
  if (retval == noErr)
    *created_dir_id = hpb.fileParam.ioDirID;
  return retval;
}

P1 (PUBLIC pascal trap, OSErr, FSpDelete,
    FSSpecPtr, spec)
{
  OSErr retval;
  HParamBlockRec hpb;

  hpb.ioParam.ioVRefNum = spec->vRefNum;
  hpb.fileParam.ioDirID = spec->parID;
  hpb.ioParam.ioNamePtr = (StringPtr) RM ((Ptr) spec->name);
  retval = PBHDelete (&hpb, FALSE);
  return retval;
}

P2 (PUBLIC pascal trap, OSErr, FSpGetFInfo,
    FSSpecPtr, spec, FInfo *, fndr_info)
{
  OSErr retval;
  HParamBlockRec hpb;

  hpb.fileParam.ioVRefNum = spec->vRefNum;
  hpb.fileParam.ioDirID = spec->parID;
  hpb.fileParam.ioNamePtr = (StringPtr) RM ((Ptr) spec->name);
  hpb.fileParam.ioFDirIndex = CWC (0);
  retval = PBHGetFInfo (&hpb, FALSE);
  if (retval == noErr)
    *fndr_info = hpb.fileParam.ioFlFndrInfo;
  return retval;
}

P2 (PUBLIC pascal trap, OSErr, FSpSetFInfo,
    FSSpecPtr, spec, FInfo *, fndr_info)
{
  OSErr retval;
  HParamBlockRec hpb;

  warning_unimplemented ("poorly implemented: call of PBHGetFInfo wasteful");
  hpb.fileParam.ioVRefNum = spec->vRefNum;
  hpb.fileParam.ioDirID = spec->parID;
  hpb.fileParam.ioNamePtr = (StringPtr) RM ((Ptr) spec->name);
  hpb.fileParam.ioFDirIndex = CWC (0);
  retval = PBHGetFInfo (&hpb, FALSE);
  if (retval == noErr)
    {
      hpb.fileParam.ioDirID = spec->parID;
      hpb.fileParam.ioFlFndrInfo = *fndr_info;
      retval = PBHSetFInfo (&hpb, FALSE);
    }
  return retval;
}

typedef OSErrRET (*lock_procp) (HParmBlkPtr pb, BOOLEAN async);

PRIVATE OSErr
lock_helper (FSSpecPtr spec, lock_procp procp)
{
  OSErr retval;
  HParamBlockRec hpb;

  hpb.fileParam.ioVRefNum = spec->vRefNum;
  hpb.fileParam.ioDirID = spec->parID;
  hpb.fileParam.ioNamePtr = (StringPtr) RM ((Ptr) spec->name);
  retval = procp (&hpb, FALSE);
  return retval;
}

P1 (PUBLIC pascal trap, OSErr, FSpSetFLock,
    FSSpecPtr, spec)
{
  return lock_helper (spec, PBHSetFLock);
}

P1 (PUBLIC pascal trap, OSErr, FSpRstFLock,
    FSSpecPtr, spec)
{
  return lock_helper (spec, PBHRstFLock);
}

P2 (PUBLIC pascal trap, OSErr, FSpRename,
    FSSpecPtr, spec, Str255, new_name)
{
  OSErr retval;
  HParamBlockRec hpb;

  hpb.fileParam.ioVRefNum = spec->vRefNum;
  hpb.fileParam.ioDirID = spec->parID;
  hpb.fileParam.ioNamePtr = (StringPtr) RM ((Ptr) spec->name);
  hpb.ioParam.ioMisc = RM ((LONGINT) new_name);
  retval = PBHRename (&hpb, FALSE);
  return retval;
}

P2 (PUBLIC pascal trap, OSErr, FSpCatMove,
    FSSpecPtr, src, FSSpecPtr, dst)
{
  OSErr retval;
  CMovePBRec cbr;

  if (src->vRefNum != dst->vRefNum)
    retval = paramErr;
  else
    {
      cbr.ioVRefNum = src->vRefNum;
      cbr.ioDirID = src->parID;
      cbr.ioNamePtr = (StringPtr) RM ((Ptr) src->name);
      cbr.ioNewName = (StringPtr) RM ((Ptr) dst->name);
      cbr.ioNewDirID = dst->parID;
      retval = PBCatMove (&cbr, FALSE);
    }
  return retval;
}

P4 (PUBLIC pascal trap, void, FSpCreateResFile,
    FSSpecPtr, spec, OSType, creator, OSType, file_type,
    ScriptCode, script)
{
  HCreateResFile_helper (CW (spec->vRefNum), CL (spec->parID),
			 spec->name, creator, file_type, script);
}


P2 (PUBLIC pascal trap, INTEGER, FSpOpenResFile,
    FSSpecPtr, spec, SignedByte, perms)
{
  INTEGER retval;

  retval = HOpenResFile (CW (spec->vRefNum), CL (spec->parID), spec->name,
			 perms);
  return retval;
}


/* NOTE: the HCreate and HOpenRF are not traps, they're just high level
   calls that are handy to use elsewhere, so they're included here. */

PUBLIC OSErr
HCreate (INTEGER vref, LONGINT dirid, Str255 name, OSType creator, OSType type)
{
  HParamBlockRec hpb;
  OSErr retval;

  hpb.fileParam.ioNamePtr = RM (name);
  hpb.fileParam.ioVRefNum = CW (vref);
  hpb.fileParam.ioDirID = CL (dirid);
  retval = PBHCreate (&hpb, FALSE);
  if (retval == noErr)
    {
      hpb.fileParam.ioFDirIndex = CWC (0);
      retval = PBHGetFInfo (&hpb, FALSE);
      if (retval == noErr)
	{
	  hpb.fileParam.ioFlFndrInfo.fdCreator = CL (creator);
	  hpb.fileParam.ioFlFndrInfo.fdType = CL (type);
	  hpb.fileParam.ioDirID = CL (dirid);
	  retval = PBHSetFInfo (&hpb, FALSE);
	}
    }
  return retval;
}

PUBLIC OSErr
HOpenRF (INTEGER vref, LONGINT dirid, Str255 name, SignedByte perm,
	 INTEGER *refp)
{
  HParamBlockRec hpb;
  OSErr retval;

  hpb.fileParam.ioNamePtr = RM (name);
  hpb.fileParam.ioVRefNum = CW (vref);
  hpb.ioParam.ioPermssn = CB (perm);
  hpb.ioParam.ioMisc = CLC (0);
  hpb.fileParam.ioDirID = CL (dirid);
  retval = PBHOpenRF (&hpb, FALSE);
  if (retval == noErr)
    *refp = CW (hpb.ioParam.ioRefNum);
  return retval;
}
