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

#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_alias[] =
  "$Id: alias.c 88 2005-05-25 03:59:37Z ctm $";
#endif

#include "rsys/common.h"

#include <stdarg.h>

#include "FileMgr.h"
#include "AliasMgr.h"
#include "MemoryMgr.h"
#include "ToolboxUtil.h"

#include "rsys/file.h"
#include "rsys/hfs.h"
#include "rsys/string.h"
#if defined (CYGWIN32)
#include "win_temp.h"
#endif

#define paramErr	(-50)

/* NOTE: if we want to be more like the Mac, we should have a 'fld#',0
   resource that will have in it: type, four bytes of 0, pascal string,
   potential padding to even things up, type, four bytes of 0, ... */


PRIVATE const char *
find_sub_dir (OSType folderType)
{
  typedef struct
    {
      OSType type;
      const char *name;
    }
  sys_sub_dir_match_t;

  static sys_sub_dir_match_t matches[] =
    {
      { kPrintMonitorDocsFolderType,	"PrintMonitor Documents", },
      { kStartupFolderType,		"Startup Items", },
      { kAppleMenuFolderType,		"Apple Menu Items", },
      { kExtensionFolderType,		"Extensions", },
      { kPreferencesFolderType,		"Preferences", },
      { kControlPanelFolderType,	"Control Panels", },
      { kFontFolderType,		"Fonts", },
    };
  int i;
  const char *retval;
  
  for (i = 0; i < (int) NELEM (matches) && matches[i].type != folderType; ++i)
    ;
  if (i < (int) NELEM (matches))
    retval = matches[i].name;
  else
    retval = 0;
  return retval;
}

PRIVATE OSErr
get_sys_vref_and_dirid (INTEGER *sys_vrefp, LONGINT *sys_diridp)
{
  OSErr err;
  WDPBRec wdp;

  wdp.ioVRefNum = BootDrive;
  wdp.ioWDIndex = CWC (0);
  wdp.ioNamePtr = CLC (0);
  err = PBGetWDInfo (&wdp, FALSE);
  if (err == noErr)
    {
      *sys_vrefp = CW (wdp.ioWDVRefNum);
      *sys_diridp = CL (wdp.ioWDDirID);
    }
  return err;
}

PRIVATE OSErr
try_to_find (INTEGER vref, const char *str, INTEGER *vrefp, LONGINT *diridp)
{
  OSErr err;
  HVCB *vcbp;

  warning_trace_info ("str = '%s'", str);
  vcbp = ROMlib_vcbbybiggestunixname (str);

  if (!vcbp)
    err = nsvErr;
  else
    {
      VCBExtra *vcbextrap;
      struct stat sbuf;

      vcbextrap = (VCBExtra *) vcbp;
      warning_trace_info ("unixname = '%s'", vcbextrap->unixname);
      if (Ustat (str, &sbuf) < 0)
	{
	  warning_trace_info ("stat failed, errno = %d", errno);
	  err = ROMlib_maperrno ();
	}
      else
	{
	  warning_trace_info ("ino = %ld, ufs.ino = %ld", (long) ST_INO (sbuf),
			      (long) vcbextrap->u.ufs.ino);
	  if (vref != CW (vcbp->vcbVRefNum))
	    err = fnfErr;
	  else
	    {
	      *vrefp = vref;
	      if (ST_INO(sbuf) == vcbextrap->u.ufs.ino)
		*diridp = 2;
	      else
		*diridp = ST_INO(sbuf);
	      err = noErr;
	    }
	}
    }
  warning_trace_info ("err = %d", err);
  return err;
}

PRIVATE OSErr
look_for_volume (const char *vol_name, INTEGER *vrefp, LONGINT *diridp)
{
  OSErr retval;
  ParamBlockRec pbr;
  Str255 pvol_name;

  str255_from_c_string (pvol_name, vol_name);
  pbr.volumeParam.ioNamePtr = RM (&pvol_name[0]);
  pbr.volumeParam.ioVolIndex = CLC (-1);
  pbr.volumeParam.ioVRefNum = CWC (0);
  retval = PBGetVInfo (&pbr, FALSE);
  if (retval == noErr)
    {
      *vrefp = CW (pbr.volumeParam.ioVRefNum);
      *diridp = 2;
    }
  return retval;
}

/*
 * We call this when we haven't been able to find what we want, so we
 * try to construct something ourselves.
 */

PRIVATE OSErr
last_chance_tmp_vref_and_dirid (INTEGER vref, INTEGER *tmp_vrefp,
				LONGINT *tmp_diridp)
{
  OSErr retval;
  HParamBlockRec pb;

  memset (&pb, 0, sizeof pb);
  pb.volumeParam.ioVRefNum = CW (vref);
  retval = PBHGetVInfo (&pb, FALSE);
  if (retval == noErr)
    {
      static char *top_level_names[] =
      {
	"\3tmp",
	"\4temp",
      };
      int i;
      OSErr err;

      *tmp_vrefp = vref;

      for (i = 0, err = fnfErr;
	   err != noErr && i < NELEM (top_level_names);
	   ++i)
	{
	  CInfoPBRec hpb;

	  memset (&hpb, 0, sizeof hpb);
	  hpb.dirInfo.ioNamePtr = (StringPtr) RM (top_level_names[i]);
	  hpb.dirInfo.ioVRefNum = pb.volumeParam.ioVRefNum;
	  hpb.dirInfo.ioDrDirID = CLC (2);
	  err = PBGetCatInfo (&hpb, FALSE);
	  if (err == noErr && (hpb.hFileInfo.ioFlAttrib & ATTRIB_ISADIR))
	    *tmp_diridp = CL (hpb.dirInfo.ioDrDirID);
	}
      if (err != noErr)
	*tmp_diridp = CLC (2);
    }

  return retval;
}

PRIVATE OSErr
get_tmp_vref_and_dirid (INTEGER vref, INTEGER *tmp_vrefp, LONGINT *tmp_diridp)
{
  int i;
  OSErr retval;
  static const char *guesses[] =
    {

#if !defined (MSDOS) && !defined (CYGWIN32)
      "/tmp",
#else
#if defined (CYGWIN32)
      NULL,
#endif
      "c:/tmp",
      "c:/temp",
      "c:/"
#endif
    };


  {
    static boolean_t been_here_p = FALSE;

    if (!been_here_p)
      {
	int j;

#if defined (CYGWIN32)
	guesses[0] = win_temp ();
#endif	
	
	for (j = 0; j < (int) NELEM (guesses); ++j)
	  {
	    char *p;
	    
	    if (guesses[j])
	      {
		p = alloca (strlen (guesses[j]) + 1);
		strcpy (p, guesses[j]);
		ROMlib_automount (p);
	      }
	  }
	been_here_p = TRUE;
      }
  }

  retval = look_for_volume ("tmp:", tmp_vrefp, tmp_diridp);

  for (i = 0; retval != noErr && i < (int) NELEM (guesses); ++i)
    retval = try_to_find (vref, guesses[i], tmp_vrefp, tmp_diridp);

  if (retval != noErr)
    retval = last_chance_tmp_vref_and_dirid (vref, tmp_vrefp, tmp_diridp);

  return retval;
}

PRIVATE OSErr
test_directory (INTEGER vref, LONGINT dirid, const char *sub_dirp,
		LONGINT *new_idp)
{
  OSErr err;
  CInfoPBRec cpb;
  Str255 file_name;

  str255_from_c_string (file_name, sub_dirp);
  cpb.hFileInfo.ioNamePtr = (StringPtr) RM ((Ptr) file_name);
  cpb.hFileInfo.ioVRefNum = CW (vref);
  cpb.hFileInfo.ioFDirIndex = CWC (0);
  cpb.hFileInfo.ioDirID = CL (dirid);
  err = PBGetCatInfo (&cpb, FALSE);
  if (err == noErr && !(cpb.hFileInfo.ioFlAttrib & ATTRIB_ISADIR))
    err = dupFNErr;
  if (err == noErr)
    *new_idp = CL (cpb.dirInfo.ioDrDirID);
  return err;
}

PRIVATE OSErr
create_directory (INTEGER sys_vref, LONGINT sys_dirid, const char *sub_dirp,
		  LONGINT *new_idp)
{
  warning_unimplemented (NULL_STRING);
  return paramErr;
}

P5 (PUBLIC pascal trap, OSErr, FindFolder,
    int16, vRefNum, OSType, folderType,
    Boolean, createFolder, int16 *, foundVRefNum, int32 *, foundDirID)
{
  OSErr retval;
  const char *sub_dir;

  sub_dir = find_sub_dir (folderType);
  if (sub_dir)
    {
      INTEGER sys_vref;
      LONGINT sys_dirid, new_id;

      retval = get_sys_vref_and_dirid (&sys_vref, &sys_dirid);
      if (retval == noErr)
	{
	  retval = test_directory (sys_vref, sys_dirid, sub_dir, &new_id);
	  if (retval == fnfErr && createFolder)
	    retval = create_directory (sys_vref, sys_dirid, sub_dir, &new_id);
	  if (retval == noErr)
	    {
	      *foundVRefNum = CW (sys_vref);
	      *foundDirID = CL (new_id);
	    }
	}
    }
  else
    switch (folderType)
      {
      case kSystemFolderType:
	{
	  INTEGER sys_vref;
	  LONGINT sys_dirid;
	
	  retval = get_sys_vref_and_dirid (&sys_vref, &sys_dirid);
	  if (retval == noErr)
	    {
	    /* NOTE: IMVI 9-44 tells us to not create System Folder if it
	       doesn't already exist */
	      *foundVRefNum = CW (sys_vref);
	      *foundDirID = CL (sys_dirid);
	    }
	}
	break;
      case kDesktopFolderType:
      case kTrashFolderType:
      case kWhereToEmptyTrashFolderType:
      case kTemporaryFolderType:
/* These cases aren't properly handled, but they should allow some apps
   to get further */
	{
	  INTEGER tmp_vref;
	  LONGINT tmp_dirid;
	  
	  retval = get_tmp_vref_and_dirid (vRefNum, &tmp_vref, &tmp_dirid);
	  warning_unimplemented ("poorly implemented");
	  if (retval == fnfErr && createFolder)
	    warning_unimplemented ("Didn't attempt to create folder");
	  if (retval == noErr)
	    {
	      *foundVRefNum = CW (tmp_vref);
	      *foundDirID = CL (tmp_dirid);
	    }
	}
	break;
      default:
	warning_unexpected ("unknown folderType");
	retval = fnfErr;
	break;
      }
  return retval;
}

P3 (PUBLIC pascal trap, OSErr, NewAlias,
    FSSpecPtr, fromFile, FSSpecPtr, target,
    AliasHandle *, alias)
{
  OSErr retval;

  *alias = 0;
  warning_unimplemented ("poorly implemented");

  retval = NewAliasMinimal (target, alias);
  return retval;
}

P4 (PUBLIC pascal trap, OSErr, UpdateAlias,
    FSSpecPtr, fromFile, FSSpecPtr, target,
    AliasHandle, alias, Boolean *, wasChanged)
{
  warning_unimplemented (NULL_STRING);
  return paramErr;
}

enum
{
  FULL_PATH_TAG = 0x0002,
  TAIL_TAG = 0x0009,
};

typedef unsigned char Str27[28];

typedef struct
{
  OSType type PACKED;
  INTEGER length PACKED;
  INTEGER usually_2 PACKED;
  INTEGER usually_0 PACKED;
  Str27 volumeName LPACKED;
  LONGINT ioVCrDate PACKED;
  INTEGER ioVSigWord PACKED;
  INTEGER zero_or_one PACKED;
  LONGINT zero_or_neg_one PACKED;
  Str63 fileName LPACKED;
  LONGINT ioDirID PACKED; /* -1 full */
  LONGINT ioFlCrDat PACKED;
  OSType type_info PACKED;
  OSType creator PACKED;
  INTEGER mystery_words[10] PACKED;
}
alias_head_t;

typedef struct /* 0x0000 */
{
  INTEGER parent_length PACKED;
  unsigned char parent_bytes[1] LPACKED;
}
alias_parent_t;

typedef struct /* 0x0001 */
{
  INTEGER eight PACKED;
  LONGINT mystery_longs[2] PACKED;
}
alias_unknown_000100_t;

typedef struct /* 0x0002 */
{
  INTEGER fullpath_length PACKED;
  unsigned char fullpath_bytes[1] LPACKED;
}
alias_fullpath_t;

typedef struct /* 0x0009 */
{
  INTEGER length PACKED;
  INTEGER weird_info[12] PACKED;
  Str32 zone LPACKED;
  Str31 server LPACKED;
  Str27 volumeName LPACKED;
  Str32 network_identity_owner_name LPACKED;
  char filler_zeros[18] LPACKED;
}
alias_tail_t;

typedef struct
{
  alias_head_t *headp PACKED;
  alias_parent_t *parentp PACKED;
  alias_unknown_000100_t *unknownp PACKED;
  alias_fullpath_t *fullpathp PACKED;
  alias_tail_t *tailp PACKED;
}
alias_parsed_t;

/*
 * ResolveAlias is just a stub so we can recover the fsspecs that are stored
 * in the AppleEvent that is constructed as part of the process of launching
 * another application.  This stub doesn't look at fromFile, doesn't consider
 * the fact that the alias may point to an alias.  It won't work if a full-path
 * alias is supplied either.
 */

P4 (PUBLIC pascal trap, OSErr, ResolveAlias,
    FSSpecPtr, fromFile, AliasHandle, alias,
    FSSpecPtr, target, Boolean *, wasAliased)
{
  OSErr retval;
  alias_head_t *headp; 
  Str255 volname;
  FSSpec fs;
  HParamBlockRec pb;

  warning_unimplemented ("stub for Launch WON'T WORK WITH FULL PATH SPEC");
  retval = noErr;
  headp = (typeof (headp)) STARH (alias);
  str255assign (volname, headp->volumeName);
  fs.parID = headp->ioDirID; /* NOT VALID IF THIS IS A FULL PATH SPEC */
  str255assign (fs.name, headp->fileName); 

  pb.volumeParam.ioNamePtr = (StringPtr) RM ((Ptr) volname);
  pb.volumeParam.ioVolIndex = CLC (-1);
  pb.volumeParam.ioVRefNum = 0;
  retval = PBHGetVInfo (&pb, FALSE);
  if (retval == noErr)
    {
      fs.vRefNum = pb.volumeParam.ioVRefNum;
      *wasAliased = FALSE;
      *target = fs;
    }

  return retval;
}

P4 (PUBLIC pascal trap, OSErr, ResolveAliasFile,
    FSSpecPtr, theSpec, Boolean, resolveAliasChains,
    Boolean *, targetIsFolder, Boolean *, wasAliased)
{
  HParamBlockRec hpb;
  OSErr retval;


  memset (&hpb, 0, sizeof hpb);
  hpb.fileParam.ioNamePtr = (StringPtr) RM ((Ptr) theSpec->name);
  hpb.fileParam.ioDirID = theSpec->parID;
  hpb.fileParam.ioVRefNum = theSpec->vRefNum;
  retval = PBHGetFInfo (&hpb, FALSE);

  if (retval == noErr)
    {
      *targetIsFolder = !!(hpb.fileParam.ioFlAttrib & ATTRIB_ISADIR);
      *wasAliased = FALSE;
    }

  warning_unimplemented ("'%.*s' retval = %d, isFolder = %d", theSpec->name[0],
			 theSpec->name+1, retval, *targetIsFolder);

  return retval;
}

P8 (PUBLIC pascal trap, OSErr, MatchAlias,
    FSSpecPtr, fromFile, int32, rulesMask,
    AliasHandle, alias, int16 *, aliasCount,
    FSSpecArrayPtr, aliasList, Boolean *, needsUpdate,
    AliasFilterProcPtr, aliasFilter,
    Ptr, yourDataPtr)
{
  warning_unimplemented (NULL_STRING);
  return paramErr;
}

P3 (PUBLIC pascal trap, OSErr, GetAliasInfo,
    AliasHandle, alias, AliasTypeInfo, index,
    Str63, theString)
{
  warning_unimplemented (NULL_STRING);
  return paramErr;
}

PRIVATE int
EVENUP (n)
{
  int retval;
	
  retval = n;
  if (retval & 1)
    ++retval;
  return retval;
}

#if 0
PRIVATE OSErr
parse2 (AliasHandle ah, const void *addrs[], int count)
{
  OSErr retval;
  Size size;
	
  size = GetHandleSize ((Handle) ah);
  if (size < sizeof (alias_head_t) + sizeof (INTEGER))
    retval = paramErr;
  else if (count < 0)
    retval = paramErr;
  else
    {
      const alias_head_t *headp;
      const INTEGER *partp, *ep;
		
      headp = (alias_head_t *) STARH (ah);
      partp = (INTEGER *) (&headp[1]);
      ep = (INTEGER *) ((char *) headp + MIN (size, CW (headp->length)));
      memset (addrs, 0, count * sizeof addrs[0]);
      for (; partp < ep && *partp != CWC (-1);
	   partp = (INTEGER *) ((char *) partp + EVENUP (4 + CW (partp[1]))))
	{
	  int part;
			
	  part = CW (*partp);
	  if (part < count)
	    addrs[part] = partp + 1;
	}
      retval = *partp == CWC (-1) ? noErr : paramErr;
    }
	
  return retval;
}
#endif

PRIVATE OSErr
decompose_full_path (INTEGER path_len, Ptr fullPath, Str27 volumeName,
		     Str31 fileName)
{
  OSErr retval;
  char *first_colon;
  char *last_colon;
  char *p, *ep;
  int volume_len;
  int file_len;
	
  for (p = (char *) fullPath,
	 ep = p + path_len, first_colon = 0, last_colon = 0;
       p != ep;
       ++p)
    {
      if (*p == ':')
	{
	  if (!first_colon)
	    first_colon = p;
	  last_colon = p;
	}
    }
  if (!first_colon)
    retval = paramErr;
  else
    {
      volume_len = first_colon - (char *) fullPath;
      file_len = ep - last_colon - 1;
      if (volume_len > 27 || file_len > 31)
	retval = paramErr;
      else
	{
	  volumeName[0] = volume_len;
	  memcpy (volumeName+1, fullPath, volume_len);
	  fileName[0] = file_len;
	  memcpy (fileName+1, last_colon + 1, file_len);
	  retval = noErr;
	}
    }
  return retval;
}

PRIVATE void
init_head (alias_head_t *headp, Str27 volumeName, Str31 fileName)
{
  memset (headp, 0, sizeof *headp);
  headp->usually_2 = CWC (2);
  memcpy (headp->volumeName, volumeName, volumeName[0]+1);
  memcpy (headp->fileName, fileName, fileName[0]+1);
  headp->mystery_words[0] = CWC (-1);
  headp->mystery_words[1] = CWC (-1);
  headp->mystery_words[3] = CWC (17);
}

PRIVATE void
init_tail (alias_tail_t *tailp, Str32 zoneName, Str31 serverName,
	   Str27 volumeName)
{
  Handle h;

  memset (tailp, 0, sizeof *tailp);
  memcpy (tailp->zone, zoneName, zoneName[0]+1);
  memcpy (tailp->server, serverName, serverName[0]+1);
  memcpy (tailp->volumeName, volumeName, volumeName[0]+1);
  h = (Handle) GetString (-16096);
  if (!h)
    tailp->network_identity_owner_name[0] = 0;
  else
    {
      int name_len;
		
      name_len = MIN (GetHandleSize (h), 31);
      memcpy (tailp->network_identity_owner_name, STARH (h), name_len);
    }
  tailp->weird_info[ 0] = CWC (0x00A8);
  tailp->weird_info[ 1] = CWC (0x6166);
  tailp->weird_info[ 2] = CWC (0x706D);
  tailp->weird_info[ 5] = CWC (0x0003);
  tailp->weird_info[ 6] = CWC (0x0018);
  tailp->weird_info[ 7] = CWC (0x0039);
  tailp->weird_info[ 8] = CWC (0x0059);
  tailp->weird_info[ 9] = CWC (0x0075);
  tailp->weird_info[10] = CWC (0x0095);
  tailp->weird_info[11] = CWC (0x009E);
}

PRIVATE OSErr
assemble_pieces (AliasHandle *ahp, alias_head_t *headp, INTEGER n_pieces, ...)
{
  Size n_bytes_needed;
  va_list va;
  int i;
  Handle h;
  OSErr retval;
	
  n_bytes_needed = sizeof *headp;
  va_start (va, n_pieces);
  for (i = 0; i < n_pieces; ++i)
    {
      INTEGER tag;
      INTEGER length;
      void *p;
		
      tag = va_arg (va, int);
      length = va_arg (va, int);
      p = va_arg (va, void *);
      n_bytes_needed += sizeof (INTEGER) + sizeof (INTEGER) + EVENUP (length);
    }
  va_end (va);
  n_bytes_needed += sizeof (INTEGER) + sizeof (INTEGER);
  h = NewHandle (n_bytes_needed);
  if (!h)
    retval = MemError ();
  else
    {
      char *op;
		
      headp->length = n_bytes_needed;
      op = (char *) STARH (h);
      memcpy (op, headp, sizeof (*headp));
      op += sizeof (*headp);
      va_start (va, n_pieces);
      for (i = 0; i < n_pieces; ++i)
	{
	  INTEGER tag, tag_x;
	  INTEGER length, length_x;
	  void *p;
		
	  tag = va_arg (va, int);
	  tag_x = CW (tag);
	  length = va_arg (va, int);
	  length_x = CW (length);
	  p = va_arg (va, void *);
	  memcpy (op, &tag_x, sizeof tag_x);
	  op += sizeof tag_x;
	  memcpy (op, &length_x, sizeof length_x);
	  op += sizeof length_x;
	  memcpy (op, p, length);
	  op += length;
	  if (length & 1)
	    *op++ = 0;
	}
      va_end (va);
      memset (op, -1, sizeof (INTEGER));
      op += sizeof (INTEGER);
      memset (op, 0, sizeof (INTEGER));
		
      *ahp = (AliasHandle) RM (h);
      retval = noErr;
    }
  return retval;
}

/*
FULL_PATH_TAG, path_len, fullPath,
TAIL_TAG     , sizeof (tail)-2, &tail.weird_info)
*/

P5 (PUBLIC pascal trap, OSErr, NewAliasMinimalFromFullPath,
    INTEGER, path_len, Ptr, fullPath, Str32, zoneName, Str31, serverName,
    AliasHandle *, ahp)
{
  OSErr retval;

  warning_unimplemented ("not tested much");
  if (zoneName[0] > 32 || serverName[0] > 31 || !ahp)
    retval = paramErr;
  else
    {
      Str27 volumeName;
      Str63 fileName;
		
      retval = decompose_full_path (path_len, fullPath, volumeName, fileName);
      if (retval == noErr)
	{
	  alias_head_t head;
	  alias_tail_t tail;
			
	  init_head (&head, volumeName, fileName);
	  if (volumeName[0] < 27)
	    head.volumeName[volumeName[0]+1] = ':';
	  head.zero_or_one = CWC (1);
	  head.zero_or_neg_one = CLC (-1);
	  head.ioDirID = CLC (-1);
	  head.ioFlCrDat = CLC (0);
	  init_tail (&tail, zoneName, serverName, volumeName);
	  retval = assemble_pieces (ahp, &head, 2,
				    FULL_PATH_TAG, path_len, (void *) fullPath,
				    TAIL_TAG     , (int) sizeof (tail)-2,
				    (void *) &tail.weird_info);
			
	}
    }
	
  if (retval != noErr)
    *ahp = NULL;
	
  return retval;
}

P2 (PUBLIC pascal trap, OSErr, NewAliasMinimal,
FSSpecPtr, fsp, AliasHandle *, ahp)
{
  HParamBlockRec hpb;
  OSErr retval;
  Str27 volName;

  warning_unimplemented ("not tested much");
  memset (&hpb, 0, sizeof hpb);
  hpb.ioParam.ioNamePtr = RM (&volName[0]);
  hpb.ioParam.ioVRefNum = fsp->vRefNum;
  retval = PBHGetVInfo (&hpb, FALSE);
  if (retval == noErr)
    {
      alias_head_t head;
		
      init_head (&head, volName, fsp->name);
      head.ioVCrDate = hpb.volumeParam.ioVCrDate;
      head.ioVSigWord = hpb.volumeParam.ioVSigWord;
      memset (&hpb, 0, sizeof hpb);
      hpb.ioParam.ioNamePtr = RM (&fsp->name[0]);
      hpb.ioParam.ioVRefNum = fsp->vRefNum;
      hpb.fileParam.ioDirID = fsp->parID;
      retval = PBHGetFInfo (&hpb, FALSE);
      if (retval == noErr)
	{
	  alias_tail_t tail;
	  Handle h;
	  Str31 serverName;
			
	  head.ioDirID = hpb.fileParam.ioDirID;
	  head.ioFlCrDat = hpb.fileParam.ioFlCrDat;
	  head.type_info = hpb.fileParam.ioFlFndrInfo.fdType;
	  head.creator = hpb.fileParam.ioFlFndrInfo.fdCreator;
	  h = (Handle) GetString (-16413);
	  if (!h)
	    serverName[0] = 0;
	  else
	    {
	      int len;
				
	      len = MIN (GetHandleSize (h), 32);
	      memcpy (serverName, STARH (h), len);
	    }
	  init_tail (&tail, (StringPtr) "\1*", serverName, volName);
	  retval = assemble_pieces (ahp, &head, 1,
				    TAIL_TAG, (int) sizeof (tail)-2,
				    (void *) &tail.weird_info);
	}
    }
	
  if (retval != noErr)
    *ahp = NULL;
  return retval;
}
