/* Copyright 1986-1996 by Abacus Research and
 * Development, Inc.  All rights reserved.
 */

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

/* Forward declarations in FileMgr.h (DO NOT DELETE THIS LINE) */

#include "rsys/common.h"

#include "FileMgr.h"
#include "OSEvent.h"
#include "VRetraceMgr.h"
#include "OSUtil.h"
#include "DeviceMgr.h"
#include "MemoryMgr.h"

#include "rsys/hfs.h"

#include "rsys/file.h"
#include "rsys/glue.h"
#include "rsys/filedouble.h"
#include "rsys/segment.h"
#include "rsys/string.h"
#include "rsys/file.h"
#include "rsys/suffix_maps.h"
#include "rsys/lockunlock.h"

#include <ctype.h>

#if defined (CYGWIN32)
#include "winfs.h"
#endif

int ROMlib_nosync = 0; /* if non-zero, we don't call sync () or fsync () */

#if !defined(NDEBUG)
PUBLIC void fs_err_hook (OSErr err)
{
}
#endif

PUBLIC int ROMlib_lasterrnomapped;

#define MAX_ERRNO 50

#define install_errno(uerr, merr)	\
    do					\
  {					\
    gui_assert(uerr < NELEM(xtable));	\
    xtable[uerr] = merr;		\
  }					\
    while (FALSE);

A0(PUBLIC, OSErr, ROMlib_maperrno)				/* INTERNAL */
{
  OSErr retval;
  static OSErr xtable[MAX_ERRNO + 1];
  static char been_here = FALSE;
  int errno_save;

  if (!been_here)
    {
      int i;

      for (i = 0; i < (int) NELEM(xtable); ++i)
	xtable[i] = fsDSIntErr;

      install_errno(0,          noErr);
      install_errno(EPERM,	permErr);
      install_errno(ENOENT,	fnfErr);
      install_errno(EIO,	ioErr);
      install_errno(ENXIO,	paramErr);
      install_errno(EBADF,	fnOpnErr);
      install_errno(EAGAIN,	fLckdErr);
      install_errno(ENOMEM,	memFullErr);
      install_errno(EACCES,	permErr);
      install_errno(EFAULT,	paramErr);
      install_errno(EBUSY,	fBsyErr);
      install_errno(EEXIST,	dupFNErr);
      install_errno(EXDEV,	fsRnErr);
      install_errno(ENODEV,	nsvErr);
      install_errno(ENOTDIR,	dirNFErr);
      install_errno(EINVAL,	paramErr);
      install_errno(ENFILE,	tmfoErr);
      install_errno(EMFILE,	tmfoErr);
      install_errno(EFBIG,	dskFulErr);
      install_errno(ENOSPC,	dskFulErr);
      install_errno(ESPIPE,	posErr);
      install_errno(EROFS,	wPrErr);
      install_errno(EMLINK,	dirFulErr);
#if !defined(MSDOS) && !defined(CYGWIN32)
      install_errno(ETXTBSY,	fBsyErr);
      install_errno(EWOULDBLOCK,permErr);
#endif

      been_here = TRUE;
    }

  errno_save = errno;
  ROMlib_lasterrnomapped = errno_save;
  
  if (errno_save < 0 || errno_save >= (int) NELEM(xtable))
    retval = fsDSIntErr;
  else
    retval = xtable[errno_save];

  if (retval == fsDSIntErr)
    warning_unexpected ("fsDSIntErr errno = %d", errno_save);

  if (retval == dirNFErr)
    warning_trace_info ("dirNFErr errno = %d", errno_save);

  fs_err_hook (retval);
  return retval;
}

A3(PUBLIC, OSErr, FSOpen, StringPtr, filen, INTEGER, vrn,	/* IMIV-109 */
								INTEGER *, rn)
{
    ParamBlockRec pbr;
    OSErr temp;

    pbr.ioParam.ioNamePtr = RM(filen);
    pbr.ioParam.ioVRefNum = CW(vrn);
    pbr.ioParam.ioVersNum = 0;
    pbr.ioParam.ioPermssn = fsCurPerm;
    pbr.ioParam.ioMisc = 0;
    temp = PBOpen(&pbr, 0);
    *rn = CW(pbr.ioParam.ioRefNum);
    fs_err_hook (temp);
    return(temp);
}

A3(PUBLIC, OSErr, OpenRF, StringPtr, filen, INTEGER, vrn,	/* IMIV-109 */
								INTEGER *, rn)
{
    ParamBlockRec pbr;
    OSErr temp;

    pbr.ioParam.ioNamePtr = RM(filen);
    pbr.ioParam.ioVRefNum = CW(vrn);
    pbr.ioParam.ioVersNum = 0;
    pbr.ioParam.ioPermssn = fsCurPerm;
    pbr.ioParam.ioMisc = 0;
    temp = PBOpenRF(&pbr, 0);
    *rn = CW(pbr.ioParam.ioRefNum);
    fs_err_hook (temp);
    return(temp);
}

A3(PUBLIC, OSErr, FSRead, INTEGER, rn, LONGINT *, count,	/* IMIV-109 */
								   Ptr, buffp)
{
    ParamBlockRec pbr;
    OSErr temp;

    pbr.ioParam.ioRefNum = CW(rn);
    pbr.ioParam.ioBuffer = RM(buffp);
    pbr.ioParam.ioReqCount = CL(*count);
    pbr.ioParam.ioPosMode = CWC(fsAtMark);
    temp = PBRead(&pbr, 0);
    *count = CL(pbr.ioParam.ioActCount);
    fs_err_hook (temp);
    return(temp);
}

PUBLIC OSErr
FSReadAll (INTEGER rn, LONGINT *countp, Ptr buffp)
{
  LONGINT orig_count;
  OSErr retval;

  orig_count = *countp;
  retval = FSRead (rn, countp, buffp);
  if (retval == noErr && *countp != orig_count)
    retval = eofErr;
  return retval;
}

A3(PUBLIC, OSErr, FSWrite, INTEGER, rn, LONGINT *, count,	/* IMIV-110 */
								   Ptr, buffp)
{
    ParamBlockRec pbr;
    OSErr temp;

    pbr.ioParam.ioRefNum = CW(rn);
    pbr.ioParam.ioBuffer = RM(buffp);
    pbr.ioParam.ioReqCount = CL(*count);
    pbr.ioParam.ioPosMode = CWC(fsAtMark);
    temp = PBWrite(&pbr, 0);
    *count = CL(pbr.ioParam.ioActCount);
    fs_err_hook (temp);
    return(temp);
}

PUBLIC OSErr
FSWriteAll (INTEGER rn, LONGINT *countp, Ptr buffp)
{
  LONGINT orig_count;
  OSErr retval;

  orig_count = *countp;
  retval = FSWrite (rn, countp, buffp);
  if (retval == noErr && *countp != orig_count)
    retval = ioErr;
  return retval;
}

A2(PUBLIC, OSErr, GetFPos, INTEGER, rn, LONGINT *, filep)	/* IMIV-110 */
{
    ParamBlockRec pbr;
    OSErr temp;

    pbr.ioParam.ioRefNum = CW(rn);
    temp = PBGetFPos(&pbr, 0);
    *filep = CL(pbr.ioParam.ioPosOffset);
    fs_err_hook (temp);
    return(temp);
}

A3(PUBLIC, OSErr, SetFPos, INTEGER, rn, INTEGER, posmode,	/* IMIV-110 */
							     LONGINT, possoff)
{
    ParamBlockRec pbr;
    OSErr err;

    pbr.ioParam.ioRefNum = CW(rn);
    pbr.ioParam.ioPosMode = CW(posmode);
    pbr.ioParam.ioPosOffset = Cx(possoff);
    err = PBSetFPos(&pbr, 0);
    fs_err_hook (err);
    return err;
}

A2(PUBLIC, OSErr, GetEOF, INTEGER, rn, LONGINT *, eof)	/* IMIV-111 */
{
    ParamBlockRec pbr;
    OSErr temp;

    pbr.ioParam.ioRefNum = Cx(rn);
    temp = PBGetEOF(&pbr, 0);
    *eof = CL(pbr.ioParam.ioMisc);
    fs_err_hook (temp);
    return(temp);
}

A2(PUBLIC, OSErr, SetEOF, INTEGER, rn, LONGINT, eof)		/* IMIV-111 */
{
    ParamBlockRec pbr;
    OSErr err;

    pbr.ioParam.ioRefNum = Cx(rn);
    pbr.ioParam.ioMisc = Cx(eof);
    err = PBSetEOF(&pbr, 0);
    fs_err_hook (err);
    return (err);
}

A2(PUBLIC, OSErr, Allocate, INTEGER, rn, LONGINT *, count)	/* IMIV-112 */
{
    ParamBlockRec pbr;
    OSErr temp;

    pbr.ioParam.ioRefNum = Cx(rn);
    pbr.ioParam.ioReqCount = *count;
    temp = PBAllocate(&pbr, 0);
    *count = CL(pbr.ioParam.ioActCount);
    fs_err_hook (temp);
    return(temp);
}

A2(PUBLIC, OSErr, AllocContig, INTEGER, rn, LONGINT *, count)
{
    ParamBlockRec pbr;
    OSErr temp;

    pbr.ioParam.ioRefNum = Cx(rn);
    pbr.ioParam.ioReqCount = *count;
    temp = PBAllocContig(&pbr, 0);
    *count = CL(pbr.ioParam.ioActCount);
    fs_err_hook (temp);
    return(temp);
}

A1(PUBLIC, OSErr, FSClose, INTEGER, rn)	/* IMIV-112 */
{
    ParamBlockRec pbr;
    OSErr err;

    pbr.ioParam.ioRefNum = Cx(rn);
    err = PBClose(&pbr, 0);
    fs_err_hook (err);
    return err;
}

A2(PUBLIC, VCB *, vlookupbyname, const char *, namep,  const char *, endp)
{
    VCB *retval;
    int n;

    n = endp - namep;
    for (retval = (VCB *)MR(VCBQHdr.qHead); retval;
					    retval = (VCB *) MR(retval->qLink))
	if (n == retval->vcbVN[0] &&
			     strncmp(namep, (char *) retval->vcbVN+1, n) == 0)
	    return retval;

#if defined (MSDOS) || defined (CYGWIN32)
    for (retval = (VCB *)MR(VCBQHdr.qHead); retval;
					    retval = (VCB *) MR(retval->qLink))
	if (n == retval->vcbVN[0] &&
	                  strncasecmp(namep, (char *) retval->vcbVN+1, n) == 0)
	    return retval;
#endif
    return 0;
}

A1(PRIVATE, VCB *, vlookupbydrive, INTEGER, drive)
{
    VCB *retval;

    for (retval = (VCB *)MR(VCBQHdr.qHead); retval;
					    retval = (VCB *) MR(retval->qLink))
	if (drive == Cx(retval->vcbDrvNum))
	    return retval;
    return 0;
}

PRIVATE LONGINT cacheindex = 0x7fffffff;

A0(PUBLIC, void, ROMlib_rewinddir)
{
    cacheindex = 0x7fffffff;
}

/*
 * Blech!  I've finally caved in and am going to look for the magic
 * number in the obnoxious case of a file that starts with %XX where XX
 * are two upper case hex digits.
 */

PUBLIC BOOLEAN ROMlib_isresourcefork(const char *fullname)
{
    LONGINT fd;
    LONGINT magic;
    BOOLEAN retval;
    const char *filename;

    /* netatalk_conventions_p means we never see resource forks, because
       they're stored in a subdirectory */

    if (netatalk_conventions_p)
      retval = FALSE;
    else
      {
	filename = strrchr(fullname, '/');
	if (!filename)
	  filename = fullname;
	else
	  ++filename;
	if (filename[0] != '%')
	  retval = FALSE;
	else if (!isxdigit(filename[1]) || !isxdigit(filename[2]))
	  retval = TRUE;
	else {
	  fd = -1;
	  retval = (fd = Uopen(fullname, O_BINARY|O_RDONLY, 0)) >= 0 &&
	    read(fd, (void *) &magic, sizeof(magic)) == sizeof(magic) &&
	      (magic == CLC(DOUBLEMAGIC)
	       || magic == DOUBLEMAGIC);
	  if (fd >= 0)
	    Uclose(fd);
	}
      }
    return retval;
}

int ROMlib_no_dot_files = FALSE;

static boolean_t
dislike_name (char *namep)
{
  boolean_t retval;

  if (strcmp (namep, ".") == 0
      || strcmp (namep, "..") == 0
      || strcmp (namep, ".AppleDouble") == 0
      || strcmp (namep, ".AppleDesktop") == 0
      || (ROMlib_no_dot_files && namep[0] == '.'))
    retval = TRUE;
  else
    {
      char *buf;
      int namelen;

      namelen = strlen (namep);
      buf = alloca (namelen);
      memcpy (buf, namep, namelen);
      namelen -= ROMlib_UNIX7_to_Mac (buf, namelen);
      retval = namelen > 31;
    }
  return retval;
}

A4(PRIVATE, char *, dirindex, char *, dir, LONGINT, index, BOOLEAN, nodirectories,
							  struct stat *, sbufp)
{
    THz saveZone;
    static char *cachedir = 0, *cachedirend;
    static DIR *dirp = 0;
#if defined (USE_STRUCT_DIRECT)
    struct direct *dp;
#else
    struct dirent *dp;
#endif
    int dirnamelen;
    struct stat sbuf;

#if defined(MSDOS) || defined (CYGWIN32)
    while (dir[0] == '/' && dir[1] == '/')
	++dir;
#endif

    if (!sbufp)
	sbufp = &sbuf;
    if (index <= cacheindex || !cachedir || strcmp(dir, cachedir) != 0) {
	if (cachedir)
	    free (cachedir);
	if (dirp)
	    closedir(dirp);
	dirp = Uopendir(dir);
	if (!dirp) {
	    dp = 0;
	    goto DONE;
	}
	cacheindex = 0;
	dirnamelen = strlen(dir);

#if !defined(MAXNAMLEN)
#define MAXNAMLEN	1024
#endif /* !defined(MAXNAMLEN) */

        saveZone = TheZone;
	TheZone = SysZone;
	cachedir = malloc (dirnamelen+1+MAXNAMLEN+1);
	TheZone = saveZone;
	if (!cachedir)
/*-->*/	    return 0;
	strcpy(cachedir, dir);
	cachedirend = cachedir + dirnamelen;
    }
    *cachedirend = '/';
    do {
	if ((dp = readdir(dirp))) {
	    strcpy(cachedirend+1, dp->d_name);
	    if (!dislike_name (dp->d_name)
		&& !ROMlib_isresourcefork(cachedir)
		&& Ustat(cachedir, sbufp) == 0) {
		if (!nodirectories || !S_ISDIR (sbufp->st_mode))
		    ++cacheindex;
	    }
	}
    } while (dp && cacheindex != index);
    *cachedirend = 0;
DONE:
    return dp ? dp->d_name : 0;
}

/*
 * NOTE: we must use capital letters in tohex below.  Code depends on it.
 */

A1(PRIVATE, unsigned char, tohex, unsigned char, c)
{
  unsigned char retval;

  retval = (unsigned char) (netatalk_conventions_p ?
			      "0123456789abcdef"
			    :
			      "0123456789ABCDEF")[c & 0xF];
  return retval;
}

A3(PRIVATE, INTEGER, Mac_to_UNIX7, unsigned char *, name, INTEGER, length,
							  unsigned char *, out)
{
    unsigned char c;
    INTEGER retval;
    boolean_t last_character_was_colon;

    retval = length;
    last_character_was_colon = TRUE;
    while (--length >= 0) {
	c = *name++;
	if (NEEDTOMAP(c)) {
	    *out++ = apple_double_quote_char;
	    *out++ = tohex(c >> 4);
	    *out++ = tohex(c);
	    retval += 2;
	} else if (c == ':') {
	    if (last_character_was_colon)
	      {
		*out++ = '.';
		*out++ = '.';
		retval += 2;
	      }
	    *out++ = '/';
	} else
	    *out++ = c;
	last_character_was_colon = c == ':';
    }
    return retval;
}

A2(PUBLIC, char *, ROMlib_newunixfrommac, char *, ip, INTEGER, n)
{
    char *retval;

    if ((retval = malloc (3*n+1)))	/* worst case numbers */
	retval[Mac_to_UNIX7((unsigned char *) ip, n,
						(unsigned char *) retval)] = 0;
    return retval;
}

/*
 * ROMlib_breakoutioname's role is to examine ioNamePtr and vRefNum
 * to determine the vcbp, but since it has to look at ioNamePtr
 * anyway it makes sense to make a unix partial pathname for the
 * rest.  While we're at it, if we get passed in a working-director
 * instead of a TRUE vrn we might as well note the directory id
 * that is associated with it.
 */

/*
 * BUG Fix TODO: newunixfrommac needs to be more complex; specifically
 *		 it needs to handle :: properly (which is to go up
 *		 a directory, UNLESS it's already at the top).
 */

A5(PUBLIC, VCB *, ROMlib_breakoutioname, ParmBlkPtr, pb,	/* INTERNAL */
	    LONGINT *, diridp, char **, therestp, BOOLEAN *, fullpathp,
							   BOOLEAN, usedefault)
{
    VCB *retval;
    char *colon;
    unsigned char *p;
    INTEGER v;
    wdentry *wdp;

    retval = 0;
    *diridp = 0;
    *therestp = 0;
    if (fullpathp)
	*fullpathp = FALSE;
    if ((p = (unsigned char *) MR(pb->ioParam.ioNamePtr))) {
	if (p[0] > 1 && p[1] != ':') {
	    if ((colon = pstr_index_after(p, ':', SLASH_CHAR_OFFSET))) {
	        retval = vlookupbyname((char *) p+1, colon);
		if (colon - ((char *)p+1) < p[0])
		    *therestp =
			      ROMlib_newunixfrommac(colon+1,
						    p[0] + (char *) p - colon);
		if (fullpathp)
		    *fullpathp = TRUE;
	    } else
	        *therestp = ROMlib_newunixfrommac((char *) p+1, p[0]);
	} else if (p[0] > 0) {
	    if (p[1] == ':' || (p[0] == 1 && p[1] == '/'))
	        *therestp = ROMlib_newunixfrommac((char *)p+2, p[0]-1);
	    else
	        *therestp = ROMlib_newunixfrommac((char *)p+1, p[0]);
	}
    }
    if (!retval) {
	if ((v = Cx(pb->ioParam.ioVRefNum)) > 0)	/* drive number */
	    retval = vlookupbydrive(v);
	else if (v < 0) {
	    if (ISWDNUM(v)) {
		wdp = WDNUMTOWDP(v);
		*diridp = CL(wdp->dirid);
		retval  = MR(wdp->vcbp);
	    } else
		retval = ROMlib_vcbbyvrn(v);
	}
	if (!retval && (usedefault ||
			 (!pb->ioParam.ioNamePtr && !pb->ioParam.ioVRefNum))) {
	    retval  = MR(DefVCBPtr);
	    *diridp = CL(DefDirID);
        }
    }
    return retval;
}

/*
 * myscandir looks through a directory for a filename match with case not
 *	     significant.  It returns TRUE if a match was found; FALSE
 *	     otherwise.  Note, the filename specified in "rest" starts
 *	     out smashed and needs the character stored in "save" to
 *	     be filled in before it can be used, but this has to be
 *	     done after "dirname" has been used.
 */

A4(PRIVATE, BOOLEAN, myscandir, char *, dirname, char, save, char *, rest,
							      INTEGER, restlen)
{
    DIR *dirp;
#if defined (USE_STRUCT_DIRECT)
    struct direct *directp;
#else
    struct dirent *directp;
#endif
    int retval;
    LONGINT d0;

#if defined(MSDOS) || defined (CYGWIN32)
    while (dirname[0] == '/' && dirname[1] == '/')
	++dirname;
#endif

    retval = FALSE;
    if ((dirp = Uopendir(dirname))) {
	*rest = save;
	d0 = ((LONGINT) restlen << 16) | (unsigned short) restlen;
	while ((directp = readdir(dirp)))
	    if (restlen == (INTEGER) strlen (directp->d_name) &&
	       ROMlib_RelString((unsigned char *) rest,
		    (unsigned char *) directp->d_name, FALSE, TRUE, d0) == 0) {
		retval = TRUE;
		memcpy(rest, directp->d_name, restlen);
/*-->*/		break;
	}
	closedir(dirp);
    } else
	*rest = save;
    return retval;
}

A2(PRIVATE, LONGINT, weasel, char *, fullpathname, struct stat *, sbufp)
{
    char *start, *nextslash, save;
    INTEGER length;
    LONGINT retval;

#if 1
    gui_assert(fullpathname[SLASH_CHAR_OFFSET] == '/');
#else
    if (fullpathname[SLASH_CHAR_OFFSET] != '/')
      {
	DEBUG_OUT("weasel surprise %s", fullpathname);
	*(long *) -1 = -1;
      }
#endif

    if (Ustat(fullpathname, sbufp) < 0) {
	switch(errno) {
#if !defined (CYGWIN32)
	case ENOENT:
#if 0
	  /* this will only work on systems that distinguish
	     between ENOENT and ENOTDIR ... maybe it won't work
	     at all.  It doesn't work under Linux, so omit it
	     for now */
	    start = strrchr(fullpathname, '/') + 1;
	    break;
#endif
	case ENOTDIR:
	    start = fullpathname + SLASH_CHAR_OFFSET + 1;
	    break;
#endif
	default:
	    return errno;
	    break;
	}
    } else
	return 0;
    do {
	nextslash = strchr(start, '/');
	if (nextslash)
	    length = nextslash - start;
	else
	    length = strlen(start);
	save = *start;
	*start = 0;
	if (!myscandir(fullpathname, save, start, length)) {
	    return nextslash ? ENOTDIR : ENOENT;
	}
	start = nextslash + 1;
    } while (nextslash);
    if (Ustat(fullpathname, sbufp) < 0)
      {
	retval = errno;
	return retval;
      }
    else
	return 0;
}

/*
 * NOTE: As of today, ROMlib_nami always either returns an error, or leaves
 *	 a valid result in *sbufp (if sbufp is non-zero).  That means that
 *	 you can call ROMlib_nami and not have to do a stat afterward.
 */

A9(PUBLIC, OSErr, ROMlib_nami, ParmBlkPtr, pb, LONGINT, dir,	/* INTERNAL */
		   IndexType, indextype, char **, pathname, char **, filename,
   char **, endname, BOOLEAN, nodirs, VCBExtra **, vcbpp, struct stat *, sbufp)
{
    char *temp1, *temp2, *temp3;
    INTEGER index;
    StringPtr fnamep;
    Str255 fname;
    LONGINT dirid;
    char *therest;
    char *fn;
    VCBExtra *vcbp;
    datum content;
    char *vcbanddir;
    int vcbstrlen, therestlen;
    StringPtr savenamep;
    BOOLEAN fullp;
    struct stat sbuf;
    INTEGER badness;
    OSErr err;
    ALLOCABEGIN

    if (!pathname)
	pathname = &temp1;
    if (!filename)
	filename = &temp2;
    if (!endname)
	endname = &temp3;
    fnamep = 0;
    if (indextype != NoIndex) {
        savenamep = MR(pb->ioParam.ioNamePtr);
        pb->ioParam.ioNamePtr = 0;
    }
#if !defined (LETGCCWAIL)
    else
	savenamep = (StringPtr) -1;
#endif /* LETGCCWAIL */
    vcbp = (VCBExtra *) ROMlib_breakoutioname(pb, &dirid, &therest, &fullp,
									 TRUE);
    if (vcbpp)
	*vcbpp = vcbp;
    if (fullp)
	dir = 0;
    else {
	if (dir <= 0)
	    dir = dirid;
    }
    content.dsize = 0;
/*
 * Retry strategy:
 *	first time things don't work we try closing and reopening
 *	the database (badness == 1); second time we try closing,
 *	rebuilding and reopening the database (badness == 2).
 */
    badness = 0;
    /* do { */
loop:
	if (dir > 2) {
	    content = ROMlib_dbm_fetch(vcbp, dir);
	    if (!content.dptr)
	      {
		content.dsize = 0;
	      }
	}
	vcbstrlen = strlen(vcbp->unixname);
	vcbanddir = ALLOCA(vcbstrlen + content.dsize + 2);
	BlockMove((Ptr) vcbp->unixname, (Ptr) vcbanddir, (Size) vcbstrlen+1);
	if (content.dsize) {
	    if (vcbanddir[vcbstrlen-1] != '/')
	      {
		vcbanddir[vcbstrlen] = '/';
		++vcbstrlen;
	      }
	    BlockMove((Ptr) content.dptr, (Ptr) vcbanddir+vcbstrlen,
							 (Size) content.dsize);
	    vcbanddir[vcbstrlen+content.dsize] = 0;
	    if (Ustat(vcbanddir, &sbuf) < 0)
	      {
		warning_unexpected ("vcbanddir = '%s', dir = 0x%x",
				    vcbanddir, dir);
		ROMlib_dbm_delete_inode(vcbp, dir);
/*-->*/		goto loop;
	      }
	    else if (ST_INO(sbuf) != dir)
	      {
#if 1
		warning_unexpected ("ST_INO(sbuf) = 0x%lx, dir = 0x%x",
				    (unsigned long) ST_INO(sbuf), dir);
		ROMlib_dbm_delete_inode(vcbp, dir);
		{
		  LONGINT templong;

		  templong = ST_INO(sbuf);
		  ROMlib_dbm_store(vcbp, vcbanddir, &templong, TRUE);
		}
/*-->*/		goto loop;
#else
		fprintf(stderr, "ino mismatch %s %ld %d\n", vcbanddir,
			(long)ST_INO(sbuf), dir);
		++badness;
#endif
	      }
	    else
	      badness = 0;
	} else if (dir > 2)
	    ++badness;
	if (badness == 1 && dirid)
	  {
	    dir = dirid;
	    badness = 2;
	    goto loop;
	  }
    if (badness && badness <= 1)
	goto loop;
    /* } while (badness && badness <= 1); */
    if (badness)
      {
	err = dirNFErr;
	warning_trace_info ("badness = %d, dirid = %d, dir = %d, "
			    "vcbp->unixname = '%s', therest = '%s', "
			    "fullp = %d, vcbanddir = '%s'",
			    badness, dirid, dir,
			    vcbp->unixname, therest, fullp, vcbanddir);
	fs_err_hook (err);
	free (therest);
/*-->*/	return err;
      }
    if (indextype != NoIndex)
       pb->ioParam.ioNamePtr = RM(savenamep);
    if (indextype == FDirIndex && (index = Cx(pb->fileParam.ioFDirIndex)) > 0) {
	free (therest);
	therest = 0;
	if (!(fn = dirindex(vcbanddir, index, nodirs, sbufp))) {
	    ALLOCAEND
	    err = fnfErr;
	    fs_err_hook (err);
/*-->*/	    return err;
	}
	if (pb->ioParam.ioNamePtr)
	    fnamep = MR(pb->ioParam.ioNamePtr);
	else
	    fnamep = fname;
	fnamep[0] = strlen(fn);
	strcpy((char *) fnamep+1, fn);
#if 0
	therest = ROMlib_newunixfrommac((char *) fnamep+1, fnamep[0]);
#else
	therest = malloc ((Size) fnamep[0] + 1);
	BlockMove((Ptr) fnamep+1, (Ptr) therest, (Size) fnamep[0]);
	therest[fnamep[0]] = 0;
#endif
    }
    therestlen = therest ? strlen(therest) : 0;
    *pathname = malloc (therestlen + strlen(vcbanddir)+2);
    *filename = *pathname + strlen(vcbanddir) +1;
    *endname = *filename + therestlen + 1;
    BlockMove((Ptr) vcbanddir, (Ptr) *pathname, (Size) strlen(vcbanddir));
    if (therest) {
	if (vcbanddir[1 + SLASH_CHAR_OFFSET])		/* i.e. test for "/" */
	    (*filename)[-1] = '/';
	else {
	    --*filename;
	    --*endname;
	}
	BlockMove((Ptr) therest, (Ptr) *filename, (Size) therestlen);
	(*endname)[-1] = 0;
    } else {
	*endname = *filename;
	(*endname)[-1] = 0;
    }
    if (strcmp(*pathname + SLASH_CHAR_OFFSET, "/") != 0) {
	while ((*endname)[-2] == '/') {	/* remove trailing slashes */
	    (*endname)[-2] = 0;
	    --*endname;
	}
	if ((*filename = strrchr(*pathname, '/')))
	    ++*filename;
	else
	    *filename = *pathname;
    } else
	*filename = *pathname;
    if (therest)
	free (therest);
    ALLOCAEND
    if (indextype == NoIndex && sbufp) {
	errno = weasel(*pathname, sbufp);
	err = ROMlib_maperrno();
    } else if (indextype == IGNORENAME && sbufp)
      {
	if (Ustat(*pathname, sbufp) < 0)
	  {
	    err = ROMlib_maperrno();
	  }
	else
	  err = noErr;
      }
    else
	err = noErr;
    fs_err_hook (err);
    if (err == noErr && fullp)
      ROMlib_automount (*pathname);
    return err;
}

/*
 * Old algorithm:
 *
 * The new code is a little bit tricky; it would be just prepend a %, except
 * if someone has a name that begins with two hex digits ("feed me") you would
 * get something that could be a data fork, so we make sure that if the first
 * char is a % and the next two are hex and the hex would make something that
 * we would have wanted to map then we map the second character even though
 * we normally wouldn't map it.
 *
 * New algorithm:
 *
 * We just prepend the bloody "%" -- We get into trouble with other providers
 * of Apple Double if we do it differently.  What a crock.
 */

#define ROOTS_PERCENT_FILE "%%2F"

A3(PUBLIC, char *, ROMlib_resname, char *, pathname,		/* INTERNAL */
					    char *, filename, char *, endname)
{
    int pathnamesize, filenamesize, newsize;
    char *newname;

    pathnamesize = filename - pathname;
    filenamesize = endname - filename;
    if (pathnamesize)
      {
	newsize = pathnamesize + filenamesize
	  + apple_double_fork_prefix_length;
	newname = malloc (newsize);
	memcpy (newname, pathname, pathnamesize);
	strcpy (&newname[pathnamesize], apple_double_fork_prefix);
	memcpy (newname + pathnamesize + apple_double_fork_prefix_length,
		filename, filenamesize);
      }
    else
      {
	/* "E:/" --> "E:/%%2F" */
	newsize = filenamesize + sizeof (ROOTS_PERCENT_FILE);
	newname = malloc (newsize);
	sprintf (newname, "%s%s", filename, ROOTS_PERCENT_FILE);
      }
    return newname;
}

/*
 * macopen opens the file using mac permissions, returning an OSErr
 * the file descriptor of the opened file is filled in where fdp points
 */

A5(PRIVATE, OSErr, macopen, char *, file, short, perm, LONGINT *, fdp,
						  BOOLEAN, isres, Byte *,flagp)
{
    int newperm;
    int flockret;
    boolean_t open_should_fail_p;
    OSErr err;

    switch (perm) {
    default:
	warning_unexpected ("perm = %d", perm);
	/* FALL THROUGH */
    case fsCurPerm:
	newperm = O_RDWR;	/* start out here, downgrade if necessary */
	*flagp |= fcwriteperm;
	break;
    case fsRdPerm:
	newperm = O_RDONLY;
	break;
    case fsWrPerm:
	*flagp |= fcwriteperm;
	newperm = isres ? O_RDWR : O_WRONLY;
	break;
    case fsRdWrPerm:
	*flagp |= fcwriteperm;
	newperm = O_RDWR;
	break;
    case fsRdWrShPerm:
	*flagp |= fcwriteperm;
	newperm = O_RDWR;
	break;
    }

#if defined(MSDOS)
#define BROKEN_OPEN
#endif

#if defined(BROKEN_OPEN)
    {
      struct stat sbuf;

      open_should_fail_p = ((newperm == O_WRONLY || newperm == O_RDWR)
			    && Ustat(file, &sbuf) == 0
			    && (!(sbuf.st_mode & S_IWUSR)));
    }
#else
    open_should_fail_p = FALSE;
#endif
    if (open_should_fail_p)
      *fdp = -1;
    else
      *fdp = Uopen(file, O_BINARY|newperm, 0);
    if (*fdp == -1 && perm == fsCurPerm) {
	*fdp = Uopen(file, O_BINARY|O_RDONLY, 0);
	*flagp &= ~fcwriteperm;
    }
    flockret = 0;
#if !defined(AVIION) && !defined(MACBLITZ) && 0
    if (*fdp != -1) {
	if (perm == fsCurPerm || perm == fsRdWrPerm)
	    flockret = flock(*fdp, LOCK_EX|LOCK_NB);
	if (perm == fsRdWrShPerm || (perm == fsCurPerm && flockret == -1))
	    flockret = flock(*fdp, LOCK_SH|LOCK_NB);
	if (flockret == -1 && perm != fsCurPerm) {
	    Uclose(*fdp);
	    *fdp = flockret;
	}
    }
#endif /* !defined(AVIION) */
    err = *fdp == -1 ? ROMlib_maperrno(): noErr;
    fs_err_hook (err);
    return err;
}

A1(PRIVATE, void, freeprn, fcbrec *, fp)
{
    fp->fdfnum = 0;
}

A1(PRIVATE, OSErr, getprn, INTEGER *, pprn)
{
    INTEGER length;
    fcbrec *fcbp, *efcbp;
    OSErr err;

/* #define	NASTYTEMPHAXK */
#if defined(NASTYTEMPHAXK) && defined(MSDOS)
static int n = 0;
*pprn = 2 + 94 * n++;
return noErr;
#endif
    length =CW( *(short *)MR(FCBSPtr));
    fcbp = (fcbrec *) ((short *)MR(FCBSPtr)+1);
    efcbp = (fcbrec *) ((char *)MR(FCBSPtr) + length);
    for (;fcbp < efcbp && fcbp->fdfnum;
			      fcbp = (fcbrec *) ((char *)fcbp + Cx(FSFCBLen)))
        ;
    if (fcbp < efcbp) {
        *pprn = (char *) fcbp - (char *) MR(FCBSPtr);
        err = noErr;
    } else
        err = tmfoErr;
    fs_err_hook (err);
    return err;
}

A4(PRIVATE, OSErr, PBOpenForkD, ParmBlkPtr, pb, BOOLEAN, a,
						  ForkType, fork, LONGINT, dir)
{
    OSErr err;
    int temp;
    INTEGER prn;
    char *pathname, *filename, *endname, *newname;
    struct stat sbuf;
    VCBExtra *vcbp;
    int namelen;
    fcbrec *fp;
    THz savezone;
    int save_index;
    char save_char;
    char *need_to_free;

    need_to_free = 0;
    pb->ioParam.ioRefNum = CWC(0);	/* in case some goofy program,
					   like StuffIt Lite decides to
					   ignore error codes */
    savezone = TheZone;
    TheZone = SysZone;
    pathname = 0;
/*
 * We have to pass in sbuf below or we won't be weaseled like we should be.
 */
    if ((err = ROMlib_nami(pb, dir, NoIndex, &pathname, &filename, &endname,
			       TRUE, &vcbp, (struct stat *) &sbuf)) == noErr) {
	err = getprn(&prn);
	if (err == noErr) {
	    fp = (fcbrec *) (MR(FCBSPtr) + prn);
	    fp->fdfnum = CL((LONGINT) ST_INO(sbuf));
	    fp->fcfd = -1;
	    fp->fcflags = 0;
	    fp->fcbTypByt = 0;
	    fp->fcbCrPs = 0;
	    if ((filename == pathname + 1) || 
		(filename == pathname + SLASH_CHAR_OFFSET + 1))
	      save_index = 0; /* root ... don't scorch leading "/" */
	    else
	      save_index = -1;
	    save_char = filename[save_index];
	    filename[save_index] = 0;
	    if (Ustat(pathname, &sbuf) < 0)	/* NOTE: this is a stat of */
		err = ROMlib_maperrno();	/* the parent's directory, */
	    else {				/* so ROMlib_nami won't do */
		if (ST_INO(sbuf) == vcbp->u.ufs.ino)	/* the stat for us */
		    fp->fcparid = CLC (2);
		else
		    fp->fcparid = CL((LONGINT) ST_INO(sbuf));
		filename[save_index] = save_char;
		if (fork == ResourceFork) {
		    newname = ROMlib_resname(pathname, filename, endname);
		    temp = Ustat(newname, &sbuf);
		    if (temp < 0 && errno != ENOENT)
			err = ioErr;
		    need_to_free = pathname; /* can't free it now, because
						filename is drawn from
						here */
		    pathname = newname;
		}
	    }
	    if (err == noErr) {
		err = macopen(pathname, pb->ioParam.ioPermssn,
		      &fp->fcfd, fork == ResourceFork, &fp->fcflags);
		if (err != noErr
		    && (fork != ResourceFork ||
			(err = ROMlib_newresfork(pathname, &fp->fcfd, TRUE))))
		    freeprn(fp);
		else {
		    if (fork == ResourceFork)
		      {
			fp->fcflags |= fcfisres;
			fp->hiddenfd = fp->fcfd;
			lseek(fp->fcfd, ROMlib_FORKOFFSET(fp), L_SET);
		      }
		    else
		      {
			newname = ROMlib_resname (pathname, filename,
						  endname);
			fp->hiddenfd = Uopen (newname, O_BINARY|O_RDWR, 0666L);
			if (fp->hiddenfd < 0)
			  {
			    Byte dummy_byte = -1;
			    
			    if (errno == ENOENT)
			      {
				if ((fp->fcflags & fcwriteperm) &&
				    !ROMlib_creator_and_type_from_filename
				    (strlen (filename), filename, NULL, NULL))
				  err = ROMlib_newresfork(newname,
							&fp->hiddenfd, TRUE);
				else
				  {
				    err = noErr;
				    fp->hiddenfd = -1;
				  }
			      }
			    else
			      macopen(newname, pb->ioParam.ioPermssn,
				      &fp->hiddenfd, TRUE, &dummy_byte);
			  }
			free (newname);
		      }
		    if (err == noErr)
			err = ROMlib_geteofostype(fp);
		    fp->fcvptr = RM((VCB *) vcbp);
		    pb->ioParam.ioRefNum = CW(prn);
		    namelen = strlen (filename);
		    namelen -= ROMlib_UNIX7_to_Mac (filename, namelen);
		    namelen = MIN(namelen, 31);
		    fp->fcname[0] = namelen;
		    BlockMove((Ptr) filename,
		      (Ptr) fp->fcname+1, (Size) namelen);
		}
	    }
	}
    }

    if (need_to_free)
      free (need_to_free);

    if (pathname)
	free (pathname);
    TheZone = savezone;
    fs_err_hook (err);
    return err;
}

A2(PUBLIC, OSErr, ufsPBOpen, ParmBlkPtr, pb, BOOLEAN, a) /* INTERNAL */
{
    OSErr err;

    err = PBOpenForkD(pb, a, DataFork, 0);
    fs_err_hook (err);
    return err;
}

A2(PUBLIC, OSErr, ufsPBHOpen, HParmBlkPtr, pb,	/* INTERNAL */
						     BOOLEAN, a) /* IMIV-136 */
{
  OSErr err;

  err = PBOpenForkD((ParmBlkPtr) pb, a, DataFork, CL(pb->fileParam.ioDirID));
  fs_err_hook (err);
  return err;
}

A2(PUBLIC trap, OSErrRET, OpenDeny, HParmBlkPtr, pb,		/* IMV-397 */
								  BOOLEAN, a)
{								/* HACK */
    HParamBlockRec block;
    OSErr retval;

    block.ioParam.ioCompletion = pb->ioParam.ioCompletion;
    block.ioParam.ioNamePtr    = pb->ioParam.ioNamePtr;
    block.ioParam.ioVRefNum    = pb->ioParam.ioVRefNum;
    block.ioParam.ioVersNum    = 0;
    switch (pb->ioParam.ioPermssn & 3) {
    case 0:
	block.ioParam.ioPermssn    = fsCurPerm;
	break;
    case 1:
	block.ioParam.ioPermssn    = fsRdPerm;
	break;
    case 2:
	block.ioParam.ioPermssn    = fsWrPerm;
	break;
    case 3:
	block.ioParam.ioPermssn    = fsRdWrPerm;
	break;
    }
    block.ioParam.ioMisc       = 0;
    block.fileParam.ioDirID = pb->fileParam.ioDirID;

    retval = PBHOpen(&block, a);
    pb->ioParam.ioResult = block.ioParam.ioResult;
    pb->ioParam.ioRefNum = block.ioParam.ioRefNum;
    fs_err_hook (retval);
    return retval;
}

A2(PUBLIC, OSErr, ufsPBOpenRF, ParmBlkPtr, pb,		/* INTERNAL */
								   BOOLEAN, a)
{
  OSErr err;

  err = PBOpenForkD(pb, a, ResourceFork, 0);
  fs_err_hook (err);
  return err;
}

A2(PUBLIC, OSErr, ufsPBHOpenRF, HParmBlkPtr, pb,	/* INTERNAL */
								   BOOLEAN, a)
{
  OSErr err;

  err = PBOpenForkD((ParmBlkPtr) pb, a, ResourceFork,
						    CL(pb->fileParam.ioDirID));
  fs_err_hook (err);
  return err;
}

#define POSMASK	0x3	/* IMIV-121 */

PRIVATE OSErr
pbfpos (ParmBlkPtr pb, LONGINT *toseekp, boolean_t can_go_past_eof)
{
    OSErr err;
    LONGINT leof;
    INTEGER forkoffset;
    LONGINT pos;
    int fd;
    fcbrec *fp;

    fp = PRNTOFPERR(Cx(pb->ioParam.ioRefNum), &err);
    if (err == noErr) {
	forkoffset = FORKOFFSET(fp);
	leof = Cx(fp->fcleof);
	fd = fp->fcfd;

	switch (Cx(pb->ioParam.ioPosMode) & POSMASK) {
	case fsAtMark:
	    if ((pos = lseek(fd, 0L, L_INCR)) == -1) {
		switch(errno) {
		case EBADF:
		    err = fnOpnErr;
		    break;
		case EINVAL:
		    err = paramErr;
		    break;
		default:
		    err = ioErr;
		}
	    } else
		*toseekp = pos - forkoffset;
	    break;
	case fsFromStart:
	    *toseekp = Cx(pb->ioParam.ioPosOffset);
	    break;
	case fsFromLEOF:
	    *toseekp = leof + Cx(pb->ioParam.ioPosOffset);
	    break;
	case fsFromMark:
	    if ((pos = lseek(fd, 0L, L_INCR)) == -1) {
		switch(errno) {
		case EBADF:
		    err = fnOpnErr;
		    break;
		case EINVAL:
		    err = paramErr;
		    break;
		default:
		    err = ioErr;
		}
	    } else
		*toseekp = pos + Cx(pb->ioParam.ioPosOffset) - forkoffset;
	    break;
	}
	if (*toseekp + forkoffset < 0) {
	    err = posErr;
	    *toseekp = 0;
	} else if ((*toseekp > leof) && !can_go_past_eof) {
	    err = eofErr;
	    *toseekp = leof;
	}
	*toseekp += forkoffset;
    }
    fs_err_hook (err);
    return err;
}

A3(PRIVATE, OSErr, PBLockUnlockRange, ParmBlkPtr, pb, BOOLEAN, a,
   lockunlock_t, op)
{
    OSErr err;
    LONGINT toseek;
    int fd;
    LONGINT curseek;
    fcbrec *fp;

    err = noErr;
    fp = PRNTOFPERR(Cx(pb->ioParam.ioRefNum), &err);
    if (err == noErr) {
	if ((err = pbfpos(pb, &toseek, TRUE)) == noErr) {
	    OSErr (*verify ) (int fd, uint32 start, uint32 count);
	    OSErr (*cleanup) (int fd, uint32 start, uint32 count);

	    fd = fp->fcfd;
	    curseek = lseek(fd, 0, L_SET);

	    if (op == lock)
	      {
		verify = ROMlib_fd_range_overlap;
		cleanup = ROMlib_fd_add_range;
	      }
	    else
	      {
		verify = ROMlib_find_fd_start_count;
		cleanup = ROMlib_fd_remove_range;
	      }

	    err = verify (fd, toseek, CL (pb->ioParam.ioReqCount));
	    if (err == noErr && lseek(fd, toseek, L_SET) == -1)
		err = ROMlib_maperrno();
	    if (err == noErr)
	      err = ROMlib_lockunlockrange (fd, toseek,
					    CL (pb->ioParam.ioReqCount), op);
	    if (err == noErr)
	      err = cleanup (fd, toseek, CL (pb->ioParam.ioReqCount));
	    lseek(fd, curseek, L_SET);
	}
    }
    fs_err_hook (err);
    return err;
}
    
A2(PUBLIC, OSErr, ufsPBLockRange, ParmBlkPtr, pb,	/* INTERNAL */
								   BOOLEAN, a)
{
    OSErr err;

    err = PBLockUnlockRange(pb, a, lock);
    fs_err_hook (err);
    return err;
}

A2(PUBLIC, OSErr, ufsPBUnlockRange, ParmBlkPtr, pb,	/* INTERNAL */
								   BOOLEAN, a)
{
    OSErr err;

    err = PBLockUnlockRange(pb, a, unlock);
    fs_err_hook (err);
    return err;
}

PRIVATE OSErr
pbsetfpos (ParmBlkPtr pb, boolean_t can_go_past_eof)
{
    OSErr err;
    INTEGER forkoffset;
    LONGINT toseek;
    int fd;
    fcbrec *fp;

    fp = PRNTOFPERR(Cx(pb->ioParam.ioRefNum), &err);
    if (err == noErr) {
	forkoffset = FORKOFFSET(fp);
	err = pbfpos(pb, &toseek, can_go_past_eof);
	fd = fp->fcfd;
	pb->ioParam.ioPosOffset = CL(lseek(fd, toseek, L_SET) - forkoffset);
    }
    fs_err_hook (err);
    return err;
}

PUBLIC int ROMlib_newlinetocr = TRUE;

/*
 * NOTE: ROMlib_destroy_blocks is a wrapper routine that either destroys
 *	 a limited range or flushes the entire cache.  Apple's semantics
 *	 say that you must flush the entire cache in certain circumstances.
 *	 However, 99% of all programs need to destroy only a range of
 *	 addresses.
 */

PUBLIC int ROMlib_flushoften = 0;

PUBLIC unsigned long ROMlib_destroy_blocks(syn68k_addr_t start, uint32 count,
					   BOOLEAN flush_only_faulty_checksums)
{
  unsigned long num_blocks_destroyed;

  if (ROMlib_flushoften)
    {
      start = 0;
      count = ~0;
    }
  if (count)
    {
      if (flush_only_faulty_checksums)
	num_blocks_destroyed =
	  destroy_blocks_with_checksum_mismatch (start, count);
      else
	num_blocks_destroyed =  destroy_blocks (start, count);
    }
  else
    num_blocks_destroyed = 0;

  return num_blocks_destroyed;
}

A2(PUBLIC, OSErr, ufsPBRead, ParmBlkPtr, pb, BOOLEAN, a) /* INTERNAL */
{
    OSErr err;
    LONGINT nread;
    INTEGER forkoffset;
    fcbrec *fp;

#if 0
    if (pb->ioParam.ioRefNum == 0x5003)
      {
	printf ("read IN  mode = %d, offset = %5d, req = %5d\n",
		CW (pb->ioParam.ioPosMode),
		CL (pb->ioParam.ioPosOffset), CL (pb->ioParam.ioReqCount));
      }
#endif

    if (Cx(pb->ioParam.ioRefNum) < 0)
      {
	err = ROMlib_dispatch(pb, a, Prime, aRdCmd);
	fs_err_hook (err);
	return err;
      }
    fp = PRNTOFPERR(Cx(pb->ioParam.ioRefNum), &err);
    if (err == noErr && ((err = pbsetfpos(pb, FALSE)) == noErr || err == eofErr)) {
	forkoffset = FORKOFFSET(fp);
	if (Cx(pb->ioParam.ioReqCount) < 0)
	    err = paramErr;
	else {
	    int fd = fp->fcfd;
	    LONGINT rc = Cx(pb->ioParam.ioReqCount);

	    if (Cx(pb->ioParam.ioPosOffset) + rc > Cx(fp->fcleof))
		rc = Cx(fp->fcleof) - Cx(pb->ioParam.ioPosOffset);
	    nread = read(fd, (char *)(MR(pb->ioParam.ioBuffer)), rc);
	    ROMlib_destroy_blocks((syn68k_addr_t)
			         (long) US_TO_SYN68K(MR(pb->ioParam.ioBuffer)),
				  rc, TRUE);
	    if (nread == -1) {
		pb->ioParam.ioActCount = 0;
		err = ioErr;
	    } else {
		pb->ioParam.ioActCount = CL(nread);
		pb->ioParam.ioPosOffset = CL(lseek(fd, 0L, L_INCR) -
								   forkoffset);
		if (rc != Cx(pb->ioParam.ioReqCount))
		    err = eofErr;
	    }
	}
    }
    fs_err_hook (err);
#if 0
    if (pb->ioParam.ioRefNum == 0x5003)
      {
	printf ("read OUT mode = %d, offset = %5d, req = %5d, act = %d, err = %d\n",
		CW (pb->ioParam.ioPosMode),
		CL (pb->ioParam.ioPosOffset), CL (pb->ioParam.ioReqCount),
		CL (pb->ioParam.ioActCount),
		err);
      }
#endif
    return err;
}

A2(PUBLIC, OSErr, ufsPBWrite, ParmBlkPtr, pb, BOOLEAN, a) /* INTERNAL */
{
    OSErr err;
    LONGINT nwrite, rc;
    INTEGER forkoffset;
    fcbrec *fp;

    if (Cx(pb->ioParam.ioRefNum) < 0)
      {
	err = ROMlib_dispatch(pb, a, Prime, aWrCmd);
	fs_err_hook (err);
	return err;
      }
    fp = PRNTOFPERR(Cx(pb->ioParam.ioRefNum), &err);
    if (err == noErr && ((err = pbsetfpos(pb, TRUE)) == noErr || err == eofErr)) {
	forkoffset = FORKOFFSET(fp);
	if ((rc = Cx(pb->ioParam.ioReqCount)) < 0)
	    err = paramErr;
	else {
	    int fd = fp->fcfd;
#if defined (WEHAVEENOUGHSWAPSPACETORUNSUNSGOOFYLITTLERPCTHINGYS)
#if defined (SUN)
	    if (lockf(fd, F_TLOCK, rc))
		err = fLckdErr;
#endif /* SUN */
#endif /* WEHAVEENOUGHSWAPSPACETORUNSUNSGOOFYLITTLERPCTHINGYS */
	    if (err == noErr && Cx(pb->ioParam.ioPosOffset) + rc > Cx(fp->fcleof)) {
		fp->fcleof = CL(CL(pb->ioParam.ioPosOffset) + rc);
		err =  ROMlib_seteof(fp);
	    }
	    if (err == noErr) {
		nwrite = write(fd, (char *)(MR(pb->ioParam.ioBuffer)), rc);
		if (nwrite == -1) {
		    pb->ioParam.ioActCount = 0;
		    err = ioErr;
		} else {
		    err = noErr;
		    pb->ioParam.ioActCount = CL(nwrite);
		    pb->ioParam.ioPosOffset = CL(lseek(fd, 0L, L_INCR) -
								   forkoffset);
		    if (Cx(pb->ioParam.ioPosOffset) > Cx(fp->fcleof)) {
			fp->fcleof = pb->ioParam.ioPosOffset;
			err = ROMlib_seteof(fp);
		    }
		}
	    }
#if defined (WEHAVEENOUGHSWAPSPACETORUNSUNSGOOFYLITTLERPCTHINGYS)
#if defined (SUN)
	    lockf(fd, F_ULOCK, Cx(pb->ioParam.ioReqCount));
#endif /* SUN */
#endif /* WEHAVEENOUGHSWAPSPACETORUNSUNSGOOFYLITTLERPCTHINGYS */
	}
    }
    fs_err_hook (err);
    return err;
}

A2(PUBLIC, OSErr, ufsPBGetFPos, ParmBlkPtr, pb,		/* INTERNAL */
								   BOOLEAN, a)
{
    OSErr err;
    INTEGER forkoffset;
    fcbrec *fp;

    fp = PRNTOFPERR(Cx(pb->ioParam.ioRefNum), &err);
    if (err == noErr) {
	forkoffset = FORKOFFSET(fp);
	pb->ioParam.ioPosOffset = CL(lseek(fp->fcfd, 0L, L_INCR) -
								   forkoffset);
	pb->ioParam.ioReqCount = pb->ioParam.ioActCount =
						     pb->ioParam.ioPosMode = 0;
    }
    fs_err_hook (err);
    return err;
}

A2(PUBLIC, OSErr, ufsPBSetFPos, ParmBlkPtr, pb,		/* INTERNAL */
								   BOOLEAN, a)
{
    OSErr err = pbsetfpos(pb, FALSE);

#if 0
    if (pb->ioParam.ioRefNum == 0x5003)
      {
	printf ("seek IN  mode = %d, offset = %5d\n",
		CW (pb->ioParam.ioPosMode),
		CL (pb->ioParam.ioPosOffset));
      }
#endif

    if (err == noErr)
	pb->ioParam.ioReqCount = pb->ioParam.ioActCount = 0;
    fs_err_hook (err);
#if 0
    if (pb->ioParam.ioRefNum == 0x5003)
      {
	printf ("seek OUT mode = %d, offset = %5d, err = %d\n",
		CW (pb->ioParam.ioPosMode),
		CL (pb->ioParam.ioPosOffset), err);
      }
#endif
    return err;
}

A2(PUBLIC, OSErr, ufsPBGetEOF, ParmBlkPtr, pb, BOOLEAN, a)	/* INTERNAL */
{
    fcbrec *fp;
    OSErr err;

    fp = PRNTOFPERR(Cx(pb->ioParam.ioRefNum), &err);
    if (err == noErr)
	pb->ioParam.ioMisc = fp->fcleof;
    fs_err_hook (err);
    return err;
}

A2(PUBLIC, OSErr, ufsPBSetEOF, ParmBlkPtr, pb, BOOLEAN, a)	/* INTERNAL */
{
    fcbrec *fp;
    OSErr err;

    fp = PRNTOFPERR(Cx(pb->ioParam.ioRefNum), &err);
    if (err == noErr) {
	fp->fcleof = pb->ioParam.ioMisc;
	err = ROMlib_seteof(fp);
    }
    fs_err_hook (err);
    return err;
}

A2(PUBLIC, OSErr, ufsPBAllocate, ParmBlkPtr, pb,	/* INTERNAL */
								   BOOLEAN, a)
{
    fcbrec *fp;
    OSErr err;

    fp = PRNTOFPERR(Cx(pb->ioParam.ioRefNum), &err);
    if (err == noErr) {
	fp->fcleof = CL(CL(fp->fcleof) + CL(pb->ioParam.ioReqCount));
	err = ROMlib_seteof(fp);
	pb->ioParam.ioActCount = pb->ioParam.ioReqCount;
    }
    fs_err_hook (err);
    return err;
}

A2(PUBLIC, OSErr, ufsPBAllocContig, ParmBlkPtr, pb,	/* INTERNAL */
								   BOOLEAN, a)
{
  OSErr err;

  /* WARN that calling this might be a sign of a non-portable program */
  err = PBAllocate(pb, a);	/* o.k. a lie, but wtf */
  fs_err_hook (err);
  return err;
}

A2(PUBLIC, OSErr, ufsPBFlushFile, ParmBlkPtr, pb,	/* INTERNAL */
								   BOOLEAN, a)
{
    fcbrec *fp;
    OSErr err;

    fp = PRNTOFPERR(Cx(pb->ioParam.ioRefNum), &err);
    if (!ROMlib_nosync)
      sync ();
    fs_err_hook (err);
    return err;
}

A2(PUBLIC, OSErr, ufsPBClose, ParmBlkPtr, pb, BOOLEAN, a)/* INTERNAL */
{
    OSErr err;
    fcbrec *fp;

    if (Cx(pb->ioParam.ioRefNum) < 0)
      {
	err = ROMlib_dispatch(pb, a, Close, 0);
	fs_err_hook (err);
	return err;
      }
    fp = PRNTOFPERR(Cx(pb->ioParam.ioRefNum), &err);

    if (err == noErr) {
	if (Uclose(fp->fcfd) < 0)
	  {
	    err = ioErr;
	  }
	Uclose(fp->hiddenfd);
	freeprn(fp);
    }
    fs_err_hook (err);
    return err;
}

A2(PUBLIC trap, OSErrRET, PBHGetLogInInfo, HParmBlkPtr, pb, BOOLEAN, a)
{
  OSErrRET retval;

  warning_unimplemented (NULL_STRING);
  retval = paramErr;
  return retval;
}

A2(PUBLIC trap, OSErrRET, PBHGetDirAccess, HParmBlkPtr, pb, BOOLEAN, a)
{
  OSErrRET retval;

  warning_unimplemented (NULL_STRING);
  retval = paramErr;
  return retval;
}

A2(PUBLIC trap, OSErrRET, PBHCopyFile, HParmBlkPtr, pb, BOOLEAN, a)
{
  OSErrRET retval;

  warning_unimplemented (NULL_STRING);
  retval = paramErr;
  return retval;
}

A2(PUBLIC trap, OSErrRET, PBHMapName, HParmBlkPtr, pb, BOOLEAN, a)
{
  OSErrRET retval;

  warning_unimplemented (NULL_STRING);
  retval = paramErr;
  return retval;
}

A2(PUBLIC trap, OSErrRET, PBHMapID, HParmBlkPtr, pb, BOOLEAN, a)
{
  OSErrRET retval;

  warning_unimplemented (NULL_STRING);
  retval = paramErr;
  return retval;
}

A2(PUBLIC trap, OSErrRET, PBHSetDirAccess, HParmBlkPtr, pb, BOOLEAN, a)
{
  OSErrRET retval;

  warning_unimplemented (NULL_STRING);
  retval = paramErr;
  return retval;
}

A2(PUBLIC trap, OSErrRET, PBHMoveRename, HParmBlkPtr, pb, BOOLEAN, a)
{
  OSErrRET retval;

  warning_unimplemented (NULL_STRING);
  retval = paramErr;
  return retval;
}
