[comp.sources.amiga] v90i215: MSH 1.30 - Messydos File System Handler, Part03/06

Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator) (07/16/90)

Submitted-by: Olaf 'Rhialto' Seibert <U211344%HNYKUN11.BITNET@CUNYVM.CUNY.EDU>
Posting-number: Volume 90, Issue 215
Archive-name: devices/msh-1.30/part03

#!/bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 6)."
# Contents:  src/device.c src/hanfile.c
# Wrapped by tadguy@xanth on Sun Jul 15 19:59:07 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/device.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/device.c'\"
else
echo shar: Extracting \"'src/device.c'\" \(18541 characters\)
sed "s/^X//" >'src/device.c' <<'END_OF_FILE'
X/*-
X * $Id: device.c,v 1.30 90/06/04 23:18:39 Rhialto Rel $
X * $Log:	device.c,v $
X * Revision 1.30  90/06/04  23:18:39  Rhialto
X * Release 1 Patch 3
X * 
X * DEVICE.C
X *
X * The messydisk.device code that makes it a real Exec .device.
X * Mostly based on the 1.1 RKM example and Matt Dillon's library code.
X *
X * This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May
X * not be used or copied without a licence.
X-*/
X
X#include "dev.h"
X#include "device.h"
X
X/*#undef DEBUG			/**/
X#ifdef DEBUG
X#   define	debug(x)  dbprintf x
X#else
X#   define	debug(x)
X#endif
X
X/*
X * The first executable location. This should return an error in case
X * someone tried to run you as a program (instead of loading you as a
X * device)
X */
X/* INDENT OFF */
X#asm
X	moveq.l #20,d0
X	rts
X#endasm
X/* INDENT ON */
X
X/*
X * A romtag structure. Both "exec" and "ramlib" look for this structure to
X * discover magic constants about you (such as where to start running you
X * from...).
X */
X/* INDENT OFF */
X#asm
X	public	__H0_end
X_EndCode equ	__H0_end
X	public	_RomTag
X_RomTag:
X	dc.w	$4AFC		; RTC_MATCHWORD
X	dc.l	_RomTag 	; rt_MatchTag
X	dc.l	__H0_end	; rt_EndSkip
X	dc.b	0		; rt_Flags (no RTF_AUTOINIT)
X	dc.b	VERSION 	; rt_Version
X	dc.b	3		; rt_Type  NT_DEVICE
X	dc.b	RTPRI		; rt_Pri
X	dc.l	_DevName	; rt_Name
X	dc.l	_idString	; rt_IdString
X	dc.l	_Init		; rt_Init
X#endasm
X/* INDENT ON */
X
Xchar		DevName[] = "messydisk.device";
Xchar		idString[] = "messydisk.device $Revision: 1.30 $ $Date: 90/06/04 23:18:39 $\r\n";
X
X/*
X * -30-6*X  Library vectors:
X */
X
Xvoid		(*LibVectors[]) () =
X{
X    _DevOpen, _DevClose, _DevExpunge, _LibNull,
X
X    _DevBeginIO,
X    _DevAbortIO,
X    (void (*) ()) -1
X};
X
X/*
X * Device commands:
X */
X
Xvoid		(*funcTable[]) () = {
X    CMD_Invalid, CMD_Reset, CMD_Read, CMD_Write, CMD_Update, CMD_Clear,
X    CMD_Stop, CMD_Start, CMD_Flush, TD_Motor, TD_Seek, TD_Format,
X    TD_Remove, TD_Changenum, TD_Changestate, TD_Protstatus, TD_Rawread,
X    TD_Rawwrite, TD_Getdrivetype, TD_Getnumtracks, TD_Addchangeint,
X    TD_Remchangeint,
X};
X
X/*
X * Here begin the system interface commands. When the user calls
X * OpenDevice/CloseDevice/RemDevice, this eventually gets trahslated into
X * a call to the following routines (Open/Close/Expunge).  Exec has
X * already put our device pointer in A6 for us.  Exec has turned off task
X * switching while in these routines (via Forbid/Permit), so we should not
X * take too long in them.
X */
X/* INDENT OFF */
X#asm
X	public	_Init
X_Init:				;a0=segment list
X	movem.l D2-D3/A0/A6,-(sp)
X	jsr	_CInit
X	movem.l (sp)+,D2-D3/A0/A6
X	rts
X
X	public	__DevOpen
X__DevOpen:			;d0=unitnum,d1=flags,a1=ioreq,a6=device
X	movem.l D0-D3/A1/A6,-(sp)
X	jsr	_DevOpen
X	movem.l (sp)+,D0-D3/A1/A6
X	rts
X
X	public	__DevClose
X__DevClose:			;a1=ioreq,a6=device
X	movem.l D2-D3/A1/A6,-(sp)
X	jsr	_DevClose
X	movem.l (sp)+,D2-D3/A1/A6
X	rts
X
X	public	__DevExpunge
X__DevExpunge:			;a6=device
X	movem.l D2-D3/A6,-(sp)
X	jsr	_DevExpunge
X	movem.l (sp)+,D2-D3/A6
X	rts
X
X	public	__LibNull
X__LibNull:
X	clr.l	d0
X	rts
X
X	public	__DevBeginIO
X__DevBeginIO:			;a1=ioreq,a6=device
X	movem.l D2-D3/A1/A6,-(sp)
X	jsr	_DevBeginIO
X	movem.l (sp)+,D2-D3/A1/A6
X	rts
X
X	public	__DevAbortIO
X__DevAbortIO:			;a1=ioreq,a6=device
X	movem.l D2-D3/A1/A6,-(sp)
X	jsr	_DevAbortIO
X	movem.l (sp)+,D2-D3/A1/A6
X	rts
X#endasm
X
X#ifdef HANDLE_IO_QUICK
X#asm
X;;;;
X;
X;   C interface to the atomic set bit and test old value instruction.
X;
X;   Called as	BSET_ACTIVE(byte *address).
X;
X;   Old value of the bit returned all over d0.w
X
X_BSET_ACTIVE:
X	move.l	4(sp),a0
X	bset	#0,(a0)         ; UNITB_ACTIVE
X	sne	d0
X	rts
X
X#endasm
X#endif
X/* INDENT ON */
X
Xlong		SysBase;	/* Argh! A global variable! */
X
X/*
X * The Initialization routine is given only a seglist pointer.	Since we
X * are NOT AUTOINIT we must construct and add the device ourselves and
X * return either NULL or the device pointer.  Exec has Forbid() for us
X * during the call.
X *
X * If you have an extended device structure you must specify the size of the
X * extended structure in MakeLibrary().
X */
X
XDEV	       *
XCInit(D2, D3, segment)
Xulong		D2,
X		D3;
Xlong		segment;
X{
X    DEV 	   *dev;
X
X    SysBase = *(long *) 4;
X#ifdef DEBUG
X    dbinit();
X#endif
X    dev = MakeLibrary(LibVectors, NULL, NULL, (long) sizeof (DEV), NULL);
X    if (DevInit(dev)) {
X	dev->dev_Node.ln_Type = NT_DEVICE;
X	dev->dev_Node.ln_Name = DevName;
X	dev->dev_Flags = LIBF_CHANGED | LIBF_SUMUSED;
X	dev->dev_Version = VERSION;
X	dev->dev_Revision = REVISION;
X	dev->dev_IdString = (APTR) idString;
X	dev->md_Seglist = segment;
X	AddDevice(dev);
X	return (dev);
X    }
X    FreeMem((char *) dev - dev->dev_NegSize, dev->dev_NegSize + dev->dev_PosSize);
X    return NULL;
X}
X
X/*
X * Open is given the device pointer, unitno and flags.	Either return the
X * device pointer or NULL.  Remove the DELAYED-EXPUNGE flag. Exec has
X * Forbid() for us during the call.
X */
X
Xvoid
XDevOpen(unitno, flags, D2, D3, ioreq, dev)
Xulong		unitno;
Xulong		flags;
Xulong		D2,
X		D3;
Xstruct IOStdReq *ioreq;
XDEV	       *dev;
X{
X    UNIT	   *unit;
X
X    debug(("OpenDevice unit %ld, flags %lx\n", unitno, flags));
X    if (unitno >= MD_NUMUNITS)
X	goto error;
X
X    if ((unit = dev->md_Unit[unitno]) == NULL) {
X	if ((unit = UnitInit(dev, unitno)) == NULL)
X	    goto error;
X	dev->md_Unit[unitno] = unit;
X    }
X    ioreq->io_Unit = (struct Unit *) unit;
X
X    ++unit->mu_OpenCnt;
X    ++dev->dev_OpenCnt;
X    dev->dev_Flags &= ~LIBF_DELEXP;
X
X    return;
X
Xerror:
X    ioreq->io_Error = IOERR_OPENFAIL;
X}
X
X/*
X * Close is given the device pointer and the io request.  Be sure not to
X * decrement the open count if already zero.	If the open count is or
X * becomes zero AND there is a LIBF_DELEXP, we expunge the device and
X * return the seglist.	Otherwise we return NULL.
X *
X * Note that this routine never sets LIBF_DELEXP on its own.
X *
X * Exec has Forbid() for us during the call.
X */
X
Xlong
XDevClose(D2, D3, ioreq, dev)
Xulong		D2,
X		D3;
Xstruct IOStdReq *ioreq;
XDEV	       *dev;
X{
X    UNIT	   *unit;
X
X    unit = (UNIT *) ioreq->io_Unit;
X    debug(("CloseDevice io %08lx unit %08lx\n", ioreq, unit));
X
X    /*
X     * See if the unit is still in use. If not, close it down. This may
X     * need to do an update, which requires the ioreq.
X     */
X
X    if (unit->mu_OpenCnt && --unit->mu_OpenCnt == 0) {
X	dev->md_Unit[unit->mu_UnitNr] = NULL;
X	UnitCloseDown(ioreq, dev, unit);
X    }
X    /*
X     * Make sure the ioreq is not used again.
X     */
X    ioreq->io_Unit = (void *) -1;
X    ioreq->io_Device = (void *) -1;
X
X    if (dev->dev_OpenCnt && --dev->dev_OpenCnt)
X	return (NULL);
X    if (dev->dev_Flags & LIBF_DELEXP)
X	return (DevExpunge(D2, D3, dev));
X    return (NULL);
X}
X
X/*
X * We expunge the device and return the Seglist ONLY if the open count is
X * zero. If the open count is not zero we set the DELAYED-EXPUNGE
X * flag and return NULL.
X *
X * Exec has Forbid() for us during the call.  NOTE ALSO that Expunge might be
X * called from the memory allocator and thus we CANNOT DO A Wait() or
X * otherwise take a long time to complete (straight from RKM).
X *
X * Apparently RemLibrary(lib) calls our expunge routine and would therefore
X * freeze if we called it ourselves.  As far as I can tell from RKM,
X * DevExpunge(lib) must remove the device itself as shown below.
X */
X
Xlong
XDevExpunge(D2, D3, dev)
Xulong		D2,
X		D3;
XDEV	       *dev;
X{
X    long	    Seglist;
X
X    if (dev->dev_OpenCnt) {
X	dev->dev_Flags |= LIBF_DELEXP;
X	return (NULL);
X    }
X    Remove(dev);
X    DevCloseDown(dev);          /* Should be quick! */
X#ifdef DEBUG
X    dbuninit();
X#endif
X    Seglist = dev->md_Seglist;
X    FreeMem((char *) dev - dev->dev_NegSize,
X	    (long) dev->dev_NegSize + dev->dev_PosSize);
X    return (Seglist);
X}
X
X/*
X * BeginIO entry point. We don't handle any QUICK requests, we just send
X * the request to the proper unit to handle.
X */
X
Xvoid
XDevBeginIO(D2, D3, ioreq, dev)
Xulong		D2,
X		D3;
Xregister struct IOStdReq *ioreq;
XDEV	       *dev;
X{
X    UNIT	   *unit;
X
X    /*
X     * Bookkeeping.
X     */
X    unit = (UNIT *) ioreq->io_Unit;
X    debug(("BeginIO: io %08lx dev %08lx u %08lx\n", ioreq, dev, unit));
X
X    /*
X     * See if the io command is within range.
X     */
X    if (STRIP(ioreq->io_Command) > TD_LASTCOMM)
X	goto NoCmd;
X
X#ifdef HANDLE_IO_QUICK
X    Forbid();                   /* Disable(); is a bit too strong for us. */
X#endif
X
X    /*
X     * Process all immediate commands no matter what. Don't even require
X     * an exclusive lock on the unit.
X     */
X    if (IMMEDIATE & (1L << STRIP(ioreq->io_Command)))
X	goto Immediate;
X
X    /*
X     * We don't handle any QUICK I/O since that only gives trouble with
X     * message ports and so. Other devices normally would include the code
X     * below.
X     */
X#ifdef HANDLE_IO_QUICK
X    /*
X     * See if the user does not request QUICK IO. If not, it is likely to
X     * be async and therefore we don't do it sync.
X     */
X    if (!(ioreq->io_Flags & IOF_QUICK))
X	goto NoQuickRequested;
X
X    /*
X     * See if the unit is STOPPED. If so, queue the msg.
X     */
X    if (unit->mu_Flags & UNITF_STOPPED)
X	goto QueueMsg;
X
X    /*
X     * This is not an immediate command. See if the device is busy. If
X     * not, process the action in this (the caller's) context.
X     */
X    if (!BSET_ACTIVE(&unit->mu_Flags))
X	goto Immediate;
X#endif
X
X    /*
X     * We need to queue the device. Clear the QUICK flag.
X     */
XQueueMsg:
X    ioreq->io_Flags &= ~IOF_QUICK;
XNoQuickRequested:
X#ifdef HANDLE_IO_QUICK
X    Permit();                   /* Enable(); is a bit too strong for us. */
X#endif
X    PutMsg(&unit->mu_Port, ioreq);
X
X    return;
X
XImmediate:
X#ifdef HANDLE_IO_QUICK
X    Permit();                   /* Enable(); is a bit too strong for us. */
X#endif
X    debug(("BeginIO: Immediate\n"));
X    ioreq->io_Error = TDERR_NoError;
X    PerformIO(ioreq, unit);
X    return;
X
XNoCmd:
X    ioreq->io_Error = IOERR_NOCMD;
X    TermIO(ioreq);
X    return;
X
X}
X
X/*
X * Terminate an io request. Called (normally) for every BeginIO. 'Funny'
X * commands that don't call TermIO, or call it multiple times, may not be
X * properly handled unless you are careful. TD_ADDCHANGEINT and
X * TD_REMCHANGEINT are obvious examples.
X */
X
Xvoid
XTermIO(ioreq)
Xregister struct IOStdReq *ioreq;
X{
X    register UNIT  *unit;
X
X    unit = (UNIT *) ioreq->io_Unit;
X    debug(("TermIO: io %08lx u %08lx %ld %d\n", ioreq, unit,
X	   ioreq->io_Actual, ioreq->io_Error));
X
X#ifdef HANDLE_IO_QUICK
X    /*
X     * Since immediate commands don't even require an exclusive lock on
X     * the unit, don't unlock it.
X     */
X    if (IMMEDIATE & (1L << STRIP(ioreq->io_Command)))
X	goto Immediate;
X
X    /*
X     * We may need to turn the active (lock) bit off, but not if we are
X     * within the task.
X     */
X    if (unit->mu_Flags & UNITF_INTASK)
X	goto Immediate;
X
X    unit->mu_Flags &= ~UNITF_ACTIVE;
X
X    /*
X     * The task may have work to do that came in while we were processing
X     * in the caller's context.
X     */
X    if (unit->mu_Flags & UNITF_WAKETASK) {
X	unit->mu_Flags &= ~UNITF_WAKETASK;
X	WakePort(&unit->mu_Port);
X    }
X#endif
X
XImmediate:
X    /*
X     * If the quick bit is still set then wen don't need to reply the msg
X     * -- just return to the user.
X     */
X
X    if (!(ioreq->io_Flags & IOF_QUICK))
X	ReplyMsg(&ioreq->io_Message);
X
X    return;
X}
X
X/*
X * AbortIO entry point. We don't abort IO here.
X */
X
Xlong
XDevAbortIO(D2, D3, ioreq, dev)
Xulong		D2,
X		D3;
XDEV	       *dev;
Xstruct IOStdReq *ioreq;
X{
X    return 1;
X}
X
Xvoid
XWakePort(port)
Xregister struct MsgPort *port;
X{
X    Signal(port->mp_SigTask, 1L << port->mp_SigBit);
X}
X
X/*
X * This is the main loop of the Unit tasks. It must be very careful with
X * global data.
X */
X
Xvoid
XUnitTask()
X{
X    /* DEV *dev; */
X    UNIT	   *unit;
X    long	    waitmask;
X    struct IOExtTD *ioreq;
X
X    {
X	struct Task    *task,
X		       *FindTask();
X
X	task = FindTask(NULL);
X	unit = (UNIT *) task->tc_UserData;
X	/* dev = unit->mu_Dev; */
X	task->tc_UserData = NULL;
X    }
X
X    /*
X     * Now finish initializing the message ports and other signal things
X     */
X
X    {
X	byte		sigbit;
X
X	unit->mu_DiskReplyPort.mp_SigBit = AllocSignal(-1L);
X	unit->mu_DiskReplyPort.mp_Flags = PA_SIGNAL;
X
X	sigbit = AllocSignal(-1L);
X	unit->mu_Port.mp_SigBit = sigbit;
X	unit->mu_Port.mp_Flags = PA_SIGNAL;
X	waitmask = 1L << sigbit;
X
X	unit->mu_DmaSignal = AllocSignal(-1L);
X    }
X
X    for (;;) {
X	debug(("Task: Waiting... "));
X	Wait(waitmask);
X
X	/*
X	 * See if we are stopped.
X	 */
X	if (unit->mu_Flags & UNITF_STOPPED)
X	    continue;
X
X#ifdef HANDLE_IO_QUICK
X	/*
X	 * Lock the device. If it fails, we have set a flag such that the
X	 * TermIO wakes us again.
X	 */
X	unit->mu_Flags |= UNITF_WAKETASK;
X	if (BSET_ACTIVE(&unit->mu_Flags))
X	    continue;
X
X	unit->mu_Flags |= UNITF_INTASK;
X#endif
X
X	while (ioreq = (struct IOExtTD *) GetMsg(&unit->mu_Port)) {
X	    debug(("Task: io %08lx %x\n", ioreq, ioreq->iotd_Req.io_Command));
X	    ioreq->iotd_Req.io_Error = 0;
X	    PerformIO((&ioreq->iotd_Req), unit);
X	}
X
X#ifdef HANDLE_IO_QUICK
X	unit->mu_Flags &= ~(UNITF_ACTIVE | UNITF_INTASK | UNITF_WAKETASK);
X#endif
X    }
X}
X
Xvoid
XCMD_Invalid(ioreq, unit)
Xstruct IOStdReq *ioreq;
XUNIT	       *unit;
X{
X    ioreq->io_Error = IOERR_NOCMD;
X    TermIO(ioreq);
X}
X
Xvoid
XCMD_Stop(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    unit->mu_Flags |= UNITF_STOPPED;
X    TermIO(ioreq);
X}
X
Xvoid
XCMD_Start(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    unit->mu_Flags &= ~UNITF_STOPPED;
X    WakePort(&unit->mu_Port);
X    TermIO(ioreq);
X}
X
Xvoid
XCMD_Flush(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    register struct IOStdReq *req;
X
X    /* Flush our own command queue */
X    Forbid();
X    while (req = (struct IOStdReq *) GetMsg(unit->mu_Port)) {
X	req->io_Error = IOERR_ABORTED;
X	ReplyMsg(&req->io_Message);
X    }
X    Permit();
X
X    WakePort(&unit->mu_Port);
X    TermIO(ioreq);
X}
X
Xvoid
XTrackdiskGateway(ioreq, unit)
Xregister struct IOExtTD *ioreq;
XUNIT	       *unit;
X{
X    register struct IOExtTD *tdioreq;
X
X    debug(("Trackdisk: %x ", ioreq->iotd_Req.io_Command));
X    tdioreq = unit->mu_DiskIOReq;
X
X    /*
X     * Clone almost the entire io request to relay to the
X     * trackdisk.device.
X     */
X
X    tdioreq->iotd_Req.io_Command = ioreq->iotd_Req.io_Command;
X    tdioreq->iotd_Req.io_Flags = ioreq->iotd_Req.io_Flags | IOF_QUICK;
X    tdioreq->iotd_Req.io_Length = ioreq->iotd_Req.io_Length;
X    tdioreq->iotd_Req.io_Data = ioreq->iotd_Req.io_Data;
X    tdioreq->iotd_Req.io_Offset = ioreq->iotd_Req.io_Offset;
X    if (ioreq->iotd_Req.io_Command & TDF_EXTCOM) {
X	tdioreq->iotd_Count = ioreq->iotd_Count;
X	tdioreq->iotd_SecLabel = ioreq->iotd_SecLabel;
X    }
X    BeginIO(tdioreq);
X    WaitIO(tdioreq);
X
X    ioreq->iotd_Req.io_Error = tdioreq->iotd_Req.io_Error;
X    ioreq->iotd_Req.io_Actual = tdioreq->iotd_Req.io_Actual;
X
X    TermIO(ioreq);
X}
X
X#ifdef DEBUG
X/* DEBUGGING			    */
Xstruct MsgPort *Dbport; 	/* owned by the debug process	    */
Xstruct MsgPort *Dback;		/* owned by the DOS device driver  */
Xshort		DBEnable;
Xstruct SignalSemaphore PortUse;
X
X#define CTOB(x) (void *)(((long)(x))>>2)        /* BCPL conversion */
X
X/*
X * DEBUGGING CODE.	You cannot make DOS library calls that access
X * other devices from within a device driver because the caller may not be
X * a process.  If you need to make such calls you must create a port and
X * construct the DOS messages yourself.  I do not do this.  To get
X * debugging info out another PROCESS is created to which debugging
X * messages can be sent. The replyport gets a new SigTask for every
X * dbprintf call, therefore the semaphore.
X */
X
Xextern void	debugproc();
Xstruct Library *DOSBase,
X	       *OpenLibrary();
X
Xdbinit()
X{
X    struct Task    *task = FindTask(NULL);
X
X    DOSBase = OpenLibrary("dos.library", 0L);
X    Dback = CreatePort("Dback", -1L);
X    FreeSignal((long) Dback->mp_SigBit);
X    Dback->mp_SigBit = 2;
X    InitSemaphore(&PortUse);
X    CreateProc("messydisk_DB", (long) TASKPRI + 1, CTOB(debugproc), 2000L);
X    WaitPort(Dback);            /* handshake startup    */
X    GetMsg(Dback);              /* remove dummy msg     */
X    DBEnable = 1;
X    dbprintf("Debugger running V1.11\n");
X}
X
Xdbuninit()
X{
X    struct Message  killmsg;
X
X    if (Dbport) {
X	killmsg.mn_Length = 0;	/* 0 means die	    */
X	ObtainSemaphore(&PortUse);
X	Dback->mp_SigTask = FindTask(NULL);
X	PutMsg(Dbport, &killmsg);
X	WaitPort(Dback);        /* He's dead jim!      */
X	GetMsg(Dback);
X	ReleaseSemaphore(&PortUse);
X	Dback->mp_SigBit = -1;
X	DeletePort(Dback);
X
X	/*
X	 * Since the debug process is running at a greater priority, I am
X	 * pretty sure that it is guarenteed to be completely removed
X	 * before this task gets control again.
X	 */
X    }
X    CloseLibrary(DOSBase);
X}
X
Xdbprintf(a, b, c, d, e, f, g, h, i, j)
Xlong		a, b, c, d, e, f, g, h, i, j;
X{
X    struct {
X	struct Message	msg;
X	char		buf[256];
X    }		    msgbuf;
X    register struct Message *msg = &msgbuf.msg;
X    register long   len;
X
X    if (Dbport && DBEnable) {
X	ObtainSemaphore(&PortUse);      /* sprintf is not re-entrant */
X	sprintf(msgbuf.buf, a, b, c, d, e, f, g, h, i, j);
X	len = strlen(msgbuf.buf) + 1;
X	msg->mn_Length = len;	/* Length NEVER 0	 */
X	Dback->mp_SigTask = FindTask(NULL);
X	PutMsg(Dbport, msg);
X	WaitPort(Dback);
X	GetMsg(Dback);
X	ReleaseSemaphore(&PortUse);
X    }
X}
X
X/*
X * BTW, the DOS library used by debugmain() was actually opened by the
X * opener of the device driver.
X */
X
Xdebugmain()
X{
X    register struct Message *msg;
X    register long   len;
X    register void  *fh,
X		   *Open();
X    void	   *fh2;
X    struct Message  DummyMsg;
X
X    Dbport = CreatePort("Dbport", -1L);
X    fh = Open("CON:0/20/640/101/Device debug", MODE_NEWFILE);
X    fh2 = Open("PAR:", MODE_OLDFILE);
X    PutMsg(Dback, &DummyMsg);
X    for (;;) {
X	WaitPort(Dbport);
X	msg = GetMsg(Dbport);
X	len = msg->mn_Length;
X	if (len == 0)
X	    break;
X	--len;			/* Fix length up	 */
X	if (DBEnable & 1)
X	    Write(fh, msg + 1, len);
X	if (DBEnable & 2)
X	    Write(fh2, msg + 1, len);
X	PutMsg(Dback, msg);
X    }
X    Close(fh);
X    Close(fh2);
X    DeletePort(Dbport);
X    PutMsg(Dback, msg);         /* Kill handshake  */
X}
X
X/*
X * The assembly tag for the DOS process:  CNOP causes alignment problems
X * with the Aztec assembler for some reason.  I assume then, that the
X * alignment is unknown.  Since the BCPL conversion basically zero's the
X * lower two bits of the address the actual code may start anywhere within
X * 8 bytes of address (remember the first longword is a segment pointer
X * and skipped).  Sigh....  (see CreateProc() above).
X */
X/* INDENT OFF */
X#asm
X	public	_debugproc
X	public	_debugmain
X
X	cseg
X_debugproc:
X	nop
X	nop
X	nop
X	nop
X	nop
X	movem.l D2-D7/A2-A6,-(sp)
X	jsr	_debugmain
X	movem.l (sp)+,D2-D7/A2-A6
X	rts
X#endasm
X
X#endif				/* DEBUG */
END_OF_FILE
if test 18541 -ne `wc -c <'src/device.c'`; then
    echo shar: \"'src/device.c'\" unpacked with wrong size!
fi
# end of 'src/device.c'
fi
if test -f 'src/hanfile.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/hanfile.c'\"
else
echo shar: Extracting \"'src/hanfile.c'\" \(19853 characters\)
sed "s/^X//" >'src/hanfile.c' <<'END_OF_FILE'
X/*-
X * $Id: hanfile.c,v 1.30 90/06/04 23:17:33 Rhialto Rel $
X * $Log:	hanfile.c,v $
X * Revision 1.30  90/06/04  23:17:33  Rhialto
X * Release 1 Patch 3
X * 
X * HANFILE.C
X *
X * The code for the messydos file system handler.
X *
X * This parts handles files and the File Allocation Table.
X *
X * This code is (C) Copyright 1989,1990 by Olaf Seibert. All rights reserved.
X * May not be used or copied without a licence.
X-*/
X
X#include "dos.h"
X#include "han.h"
X
X#ifdef HDEBUG
X#   define	debug(x)  dbprintf x
X#else
X#   define	debug(x)
X#endif
X
Xextern char	DotDot[1 + 8 + 3];
X
X/*
X * Read the FAT from the disk, and count the free clusters.
X */
X
Xint
XGetFat()
X{
X    int 	    i;
X    byte	   *secptr;
X
X    if (!Fat && !(Fat = AllocMem((long) Disk.bps * Disk.spf, BufMemType))) {
X	debug(("No memory for FAT\n"));
X	return 0;
X    }
X    FatDirty = FALSE;
X    for (i = 0; i < Disk.spf; i++) {
X	if (secptr = GetSec(Disk.res + i)) {
X	    CopyMem(secptr, Fat + i * Disk.bps, (long) Disk.bps);
X	    FreeSec(secptr);
X	} else {
X	    /* q&d way to set the entire FAT to FAT_EOF */
X	    setmem(Fat + i * Disk.bps, (int) Disk.bps, FAT_EOF);        /* 0xFF */
X	}
X    }
X
X    debug(("counting free clusters\n"));
X
X    Disk.nsectsfree = 0;
X    for (i = MS_FIRSTCLUST; i <= Disk.maxclst; i++) {
X	if (GetFatEntry((word) i) == FAT_UNUSED)
X	    Disk.nsectsfree += Disk.spc;
X    }
X
X    return 1;
X}
X
Xvoid
XFreeFat()
X{
X    if (Fat) {
X	FreeMem(Fat, (long) Disk.bps * Disk.spf);
X	Fat = NULL;
X	FatDirty = FALSE;
X    }
X}
X
X/*-
X *  The FAT consists of 12-bits entries for each cluster,
X *  indicating the next cluster in the chain, or FFF for EOF.
X *
X *  Every two entries are packed in three bytes, like this:
X *
X *  Two entries 	abc  123 (for one cluster and the next)
X *  are packed as	bc 3a 12
X-*/
X
Xword
XGetFatEntry(cluster)
Xword		cluster;
X{
X    if (!Fat && !GetFat())
X	return FAT_EOF;
X
X    if (Disk.fat16bits) {
X	return OtherEndianWord(((word *)Fat)[cluster]);
X    } else {
X	register int	offset = 3 * (cluster / 2);
X	register word	twoentries;
X
X	if (cluster & 1) {
X	    twoentries = Fat[offset + 1] >> 4;
X	    twoentries |= Fat[offset + 2] << 4;
X	} else {
X	    twoentries = Fat[offset];
X	    twoentries |= (Fat[offset + 1] & 0x0F) << 8;
X	}
X
X	/*
X	 * Convert the special values 0xFF7 .. 0xFFF to 16 bits so they
X	 * can be checked consistently.
X	 */
X	if (twoentries >= 0xFF7)
X	    twoentries |= 0xF000;
X
X	return twoentries;
X    }
X}
X
X#ifndef READONLY
X
Xvoid
XSetFatEntry(cluster, value)
Xword		cluster;
Xword		value;
X{
X    if (!Fat && !GetFat())
X	return;
X
X    if (Disk.fat16bits) {
X	((word *)Fat)[cluster] = OtherEndianWord(value);
X    } else {
X	register int	offset = 3 * (cluster / 2);
X
X	if (cluster & 1) {          /* 123 kind of entry */
X	    Fat[offset + 2] = value >> 4;
X	    Fat[offset + 1] &= 0x0F;
X	    Fat[offset + 1] |= (value & 0x0F) << 4;
X	} else {		    /* abc kind of entry */
X	    Fat[offset + 0] = value;
X	    Fat[offset + 1] &= 0xF0;
X	    Fat[offset + 1] |= (value >> 8) & 0x0F;
X	}
X    }
X
X    FatDirty = TRUE;
X}
X
X/*
X * Find a free cluster to install as the one following this one. Start
X * looking for it right after the given one, so we allocate the cluster
X * chain as contiguous as possible. If we run off the end of the disk, we
X * start again at the beginning. The termination test should not be
X * necessary (and won't work if we are given MSFIRSTCLUST - 1) but won't
X * harm either.
X */
X
Xword
XFindFreeCluster(prev)
Xword		prev;
X{
X    register word   i;
X
X    if (prev == 0 || prev == FAT_EOF)
X	prev = MS_FIRSTCLUST - 1;
X
X    if (Disk.nsectsfree >= Disk.spc) {
X	for (i = prev + 1; i != prev; i++) {
X	    if (i > Disk.maxclst)       /* Wrap around */
X		i = MS_FIRSTCLUST;
X	    if (GetFatEntry(i) == FAT_UNUSED) {
X		SetFatEntry(i, FAT_EOF);
X		if (prev >= MS_FIRSTCLUST)
X		    SetFatEntry(prev, i);
X		Disk.nsectsfree -= Disk.spc;
X		return i;
X	    }
X	}
X    }
X    return FAT_EOF;
X}
X
X/*
X * Add a cluster to a cluster chain. For input, we get some cluster we
X * know that is on the chain, even if it is the first one.
X */
X
Xword
XExtendClusterChain(cluster)
Xregister word	cluster;
X{
X    register word   nextcluster;
X
X    /*
X     * Find the end of the cluster chain to tack the new cluster on to.
X     * Then FindFreeCluster will (or won't) extend the chain for us.
X     */
X    if (cluster != 0)
X	while ((nextcluster = NextCluster(cluster)) != FAT_EOF) {
X	    cluster = nextcluster;
X	}
X
X    return FindFreeCluster(cluster);
X}
X
X/*
X * Free a chain of clusters by setting their FAT entries to FAT_UNUSED.
X */
X
Xvoid
XFreeClusterChain(cluster)
Xregister word	cluster;
X{
X    register word   nextcluster;
X
X    while (cluster != FAT_EOF) {
X	nextcluster = NextCluster(cluster);
X	SetFatEntry(cluster, FAT_UNUSED);
X	Disk.nsectsfree += Disk.spc;
X	cluster = nextcluster;
X    }
X}
X
X#endif				/* READONLY */
X
X/*
X * This routine opens a file.
X */
X
Xstruct MSFileHandle *
XMSOpen(parentdir, name, mode)
Xstruct MSFileLock *parentdir;
Xchar	       *name;
Xlong		mode;
X{
X    struct MSFileLock *fl;
X    struct MSFileHandle *fh = NULL;
X    long	    lockmode;
X
X    switch (mode) {
X    case MODE_NEWFILE:
X    case MODE_READWRITE:
X	lockmode = EXCLUSIVE_LOCK ^ MODE_CREATEFILE;
X	break;
X    default:
X	mode = MODE_OLDFILE;
X    case MODE_OLDFILE:
X	lockmode = SHARED_LOCK;
X    }
X
X    if (fl = MSLock(parentdir, name, lockmode)) {
Xmakefh:
X	if (fl->msfl_Msd.msd_Attributes & ATTR_DIR) {
X	    error = ERROR_OBJECT_WRONG_TYPE;
X	    MSUnLock(fl);
X	} else if (fh = AllocMem((long) sizeof (*fh), MEMF_PUBLIC)) {
X#ifndef READONLY
X	    /* Do we need to truncate the file? */
X	    if (mode == MODE_NEWFILE && fl->msfl_Msd.msd_Cluster) {
X		FreeClusterChain(fl->msfl_Msd.msd_Cluster);
X		fl->msfl_Msd.msd_Cluster = 0;
X		fl->msfl_Msd.msd_Filesize = 0;
X		UpdateFileLock(fl);
X	    }
X#endif
X	    fh->msfh_Cluster = fl->msfl_Msd.msd_Cluster;
X	    fh->msfh_SeekPos = 0;
X	    fh->msfh_FileLock = fl;
X	} else {
X	    error = ERROR_NO_FREE_STORE;
X	    MSUnLock(fl);
X	}
X	return fh;
X    }
X#ifndef READONLY
X    /*
X     * If the file was not found, see if we can make a new one. Therefore
X     * we need to have an empty spot in the desired directory, and create
X     * an MSFileLock for it.
X     */
X
X    if (!(lockmode & MODE_CREATEFILE) && (fl = EmptyFileLock)) {
X	debug(("Creating new file\n"));
X	EmptyFileLock = NULL;
X	fl->msfl_Msd.msd_Attributes = ATTR_ARCHIVED;
X	UpdateFileLock(fl);
X
X	goto makefh;
X    }
X    if (EmptyFileLock) {
X	MSUnLock(EmptyFileLock);
X	EmptyFileLock = NULL;
X    }
X#endif
X
X    return NULL;
X}
X
Xvoid
XMSClose(fh)
Xregister struct MSFileHandle *fh;
X{
X    if (fh) {
X	MSUnLock(fh->msfh_FileLock);
X	FreeMem(fh, (long) sizeof (*fh));
X    }
X}
X
Xlong
XMSSeek(fh, position, mode)
Xstruct MSFileHandle *fh;
Xlong		position;
Xlong		mode;
X{
X    long	    oldpos = fh->msfh_SeekPos;
X    long	    newpos = oldpos;
X    long	    filesize = fh->msfh_FileLock->msfl_Msd.msd_Filesize;
X    word	    cluster = fh->msfh_Cluster;
X    word	    oldcluster;
X    word	    newcluster;
X
X    switch (mode) {
X    case OFFSET_BEGINNING:
X	newpos = position;
X	break;
X    case OFFSET_CURRENT:
X	newpos += position;
X	break;
X    case OFFSET_END:
X	newpos = filesize - position;
X	break;
X    }
X
X    if (newpos < 0 || newpos > filesize) {
X	error = ERROR_SEEK_ERROR;
X	return -1;
X    }
X    newcluster = newpos / Disk.bpc;
X    oldcluster = oldpos / Disk.bpc;
X
X    if (oldcluster > newcluster) {      /* Seek backwards */
X	cluster = fh->msfh_FileLock->msfl_Msd.msd_Cluster;
X	oldcluster = 0;
X    }
X    if (oldcluster < newcluster) {
X	if (CheckLock(fh->msfh_FileLock))
X	    return -1L;
X	while (oldcluster < newcluster) {
X	    cluster = NextCluster(cluster);
X	    oldcluster++;
X	}
X    }
X    fh->msfh_Cluster = cluster;
X    fh->msfh_SeekPos = newpos;
X
X    return oldpos;
X}
X
Xlong
XMSRead(fh, userbuffer, size)
Xregister struct MSFileHandle *fh;
Xregister byte  *userbuffer;
Xregister long	size;
X{
X    long	    oldsize;
X
X    if (CheckLock(fh->msfh_FileLock))
X	return -1L;
X
X    if (fh->msfh_SeekPos + size > fh->msfh_FileLock->msfl_Msd.msd_Filesize)
X	size = fh->msfh_FileLock->msfl_Msd.msd_Filesize - fh->msfh_SeekPos;
X
X    oldsize = size;
X
X    while (size > 0) {
X	word		offset;
X	word		sector;
X	byte	       *diskbuffer;
X	long		insector;
X	long		tocopy;
X
X	offset = fh->msfh_SeekPos % Disk.bpc;
X	sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset);
X	if (diskbuffer = GetSec(sector)) {
X	    offset %= Disk.bps;
X	    insector = Disk.bps - offset;
X	    tocopy = lmin(size, insector);
X
X	    CopyMem(diskbuffer + offset, userbuffer, tocopy);
X	    userbuffer += tocopy;
X	    size -= tocopy;
X	    FreeSec(diskbuffer);
X	    /* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */
X	    if ((fh->msfh_SeekPos += tocopy) % Disk.bpc == 0)
X		fh->msfh_Cluster = NextCluster(fh->msfh_Cluster);
X	} else {		/* Read error. Return amount successfully
X				 * read, if any. Else return -1 for error. */
X	    if (size == oldsize) {
X		return -1L;
X	    }
X	    return oldsize - size;
X	}
X    }
X
X    return oldsize;
X}
X
Xlong
XMSWrite(fh, userbuffer, size)
Xregister struct MSFileHandle *fh;
Xregister byte  *userbuffer;
Xregister long	size;
X{
X#ifdef READONLY
X    return -1;
X#else
X    long	    oldsize;
X    struct MSFileLock *fl = fh->msfh_FileLock;
X    word	    prevclust = fl->msfl_Msd.msd_Cluster;
X    word	    update = 0;
X
X    if (CheckLock(fl))
X	return -1;
X
X    if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) {
X	error = ERROR_WRITE_PROTECTED;
X	return -1;
X    }
X
X    oldsize = size;
X
X    while (size > 0) {
X	/*
X	 * Do we need to extend the file?
X	 */
X
X	if (fh->msfh_Cluster == 0 || fh->msfh_Cluster == FAT_EOF) {
X	    word	    newclust;
X
X	    newclust = ExtendClusterChain(prevclust);
X	    debug(("Extend with %d\n", newclust));
X	    if (newclust != FAT_EOF) {
X		if (prevclust == 0) {   /* Record first cluster in dir */
X		    fl->msfl_Msd.msd_Cluster = newclust;
X		}
X		fh->msfh_Cluster = newclust;
X		prevclust = newclust;
X	    } else {
X		error = ERROR_DISK_FULL;
X		goto error;
X	    }
X	}
X	{
X	    word	    offset;
X	    word	    sector;
X	    byte	   *diskbuffer;
X	    long	    insector;
X	    long	    tocopy;
X
X	    offset = fh->msfh_SeekPos % Disk.bpc;
X	    sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset);
X	    offset %= Disk.bps;
X	    insector = Disk.bps - offset;
X	    tocopy = lmin(size, insector);
X
X	    if (tocopy == Disk.bps)
X		diskbuffer = EmptySec(sector);
X	    else
X		diskbuffer = GetSec(sector);
X
X	    if (diskbuffer != NULL) {
X		CopyMem(userbuffer, diskbuffer + offset, tocopy);
X		userbuffer += tocopy;
X		size -= tocopy;
X		MarkSecDirty(diskbuffer);
X		FreeSec(diskbuffer);
X		/* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */
X		if ((fh->msfh_SeekPos += tocopy) % Disk.bpc == 0)
X		    fh->msfh_Cluster = NextCluster(fh->msfh_Cluster);
X		if (fh->msfh_SeekPos > fl->msfl_Msd.msd_Filesize)
X		    fl->msfl_Msd.msd_Filesize = fh->msfh_SeekPos;
X		fl->msfl_Msd.msd_Attributes |= ATTR_ARCHIVED;
X		update = 1;
X	    } else {		/* Write error. */
X	error:
X		if (update)
X		    UpdateFileLock(fl);
X#if 1
X		return -1;	/* We loose the information about how much
X				 * data we wrote, but the standard file system
X				 * seems to do it this way. */
X#else
X		if (size == oldsize) {
X		    return -1;
X		}
X		return oldsize - size;	/* Amount successfully written */
X#endif
X	    }
X	}
X    }
X
X    if (update)
X	UpdateFileLock(fl);
X
X    return oldsize;
X#endif
X}
X
Xlong
XMSDeleteFile(parentdir, name)
Xstruct MSFileLock *parentdir;
Xbyte	       *name;
X{
X#ifdef READONLY
X    return DOSFALSE;
X#else
X    register struct MSFileLock *fl;
X
X    fl = MSLock(parentdir, name, EXCLUSIVE_LOCK);
X    if (fl) {
X	if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) {
X	    error = ERROR_DELETE_PROTECTED;
X	    goto error;
X	}
X	if (fl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY) {
X	    struct FileInfoBlock fib;
X
X	    /*
X	     * We normally can't get REAL exclusive locks on directories,
X	     * so we check here just to be sure. We don't want to delete
X	     * anyone's current directory, do we?
X	     */
X
X	    if (fl->msfl_Refcount != 1 || fl == RootLock) {
X		error = ERROR_OBJECT_IN_USE;
X		goto error;
X	    }
X	    if (MSExamine(fl, &fib) &&  /* directory itself */
X		MSExNext(fl, &fib)) {   /* should fail */
X		if (error == 0) {
X	    not_empty:
X		    error = ERROR_DIRECTORY_NOT_EMPTY;
X	    error:
X		    MSUnLock(fl);
X		    return DOSFALSE;
X		}
X	    }
X	    if (error != ERROR_NO_MORE_ENTRIES)
X		goto error;
X
X	    error = 0;
X	}
X	if (fl->msfl_Msd.msd_Cluster)
X	    FreeClusterChain(fl->msfl_Msd.msd_Cluster);
X	fl->msfl_Msd.msd_Name[0] = DIR_DELETED;
X	WriteFileLock(fl);
X	MSUnLock(fl);
X
X	return DOSTRUE;
X    }
X    return DOSFALSE;
X#endif
X}
X
Xlong
XMSSetDate(parentdir, name, datestamp)
Xstruct MSFileLock *parentdir;
Xbyte	       *name;
Xstruct DateStamp *datestamp;
X{
X#ifdef READONLY
X    return DOSFALSE;
X#else
X    register struct MSFileLock *fl;
X
X    fl = MSLock(parentdir, name, EXCLUSIVE_LOCK);
X    if (fl) {
X	ToMSDate(&fl->msfl_Msd.msd_Date, &fl->msfl_Msd.msd_Time, datestamp);
X	WriteFileLock(fl);
X	MSUnLock(fl);
X
X	return DOSTRUE;
X    }
X    return DOSFALSE;
X#endif
X}
X
X/*
X * Create a new directory, with its own initial "." and ".." entries.
X */
X
Xstruct MSFileLock *
XMSCreateDir(parentdir, name)
Xstruct MSFileLock *parentdir;
Xbyte	       *name;
X{
X#ifdef READONLY
X    return DOSFALSE;
X#else
X    register struct MSFileLock *fl;
X
X    /*
X     * Go create a new file. If we fail later, we have an empty file that
X     * we delete again.
X     */
X
X    fl = MSLock(parentdir, name, EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
X    if (fl || error == ERROR_OBJECT_IN_USE) {
X	error = ERROR_OBJECT_EXISTS;
X	goto error;
X    }
X    if (error != 0) {
X	goto error;
X    }
X    if (fl = EmptyFileLock) {
X	debug(("Creating new dir\n"));
X	EmptyFileLock = NULL;
X	if ((fl->msfl_Msd.msd_Cluster = FindFreeCluster(FAT_EOF)) != FAT_EOF) {
X	    struct MsDirEntry direntry;
X	    byte	   *sec;
X	    word	    sector;
X
X	    sector = ClusterToSector(fl->msfl_Msd.msd_Cluster);
X	    sec = EmptySec(sector);
X	    if (sec == NULL)
X		goto error_no_free_store;
X	    setmem(sec, (int) Disk.bps, 0);
X
X	    /*
X	     * Turn the file into a directory.
X	     */
X	    fl->msfl_Msd.msd_Attributes = ATTR_DIRECTORY | ATTR_ARCHIVED;
X	    UpdateFileLock(fl);
X
X	    /*
X	     * Create the "." entry.
X	     */
X	    direntry = fl->msfl_Msd;
X	    strncpy(direntry.msd_Name, DotDot + 1, 8 + 3);
X	    OtherEndianMsd(&direntry);
X	    ((struct MsDirEntry *) sec)[0] = direntry;
X
X	    /*
X	     * Get the real parent directory because we will duplicate the
X	     * directory entry in the subdirectory.
X	     */
X
X	    parentdir = MSParentDir(fl);
X	    if (parentdir == NULL)      /* Cannot happen */
X		parentdir = MSDupLock(RootLock);
X
X	    /*
X	     * Create the ".." entry.
X	     */
X	    direntry = parentdir->msfl_Msd;
X	    strncpy(direntry.msd_Name, DotDot, 8 + 3);
X	    direntry.msd_Attributes = ATTR_DIRECTORY | ATTR_ARCHIVED;
X	    OtherEndianMsd(&direntry);
X	    ((struct MsDirEntry *) sec)[1] = direntry;
X
X	    MSUnLock(parentdir);
X
X	    MarkSecDirty(sec);
X	    FreeSec(sec);
X
X	    /*
X	     * Clear out the rest of the newly created directory.
X	     */
X
X	    while ((sector = NextClusteredSector(sector)) != SEC_EOF) {
X		sec = EmptySec(sector);
X		if (sec == NULL)
X		    goto error_no_free_store;
X		setmem(sec, (int) Disk.bps, 0);
X		MarkSecDirty(sec);
X		FreeSec(sec);
X	    }
X	} else {
X	    MSUnLock(fl);
X	    fl = NULL;
X	    MSDeleteFile(parentdir, name);
X	    error = ERROR_DISK_FULL;
X	}
X    }
X    if (EmptyFileLock) {
X	MSUnLock(EmptyFileLock);
X	EmptyFileLock = NULL;
X    }
X    return fl;
X
Xerror_no_free_store:
X    error = ERROR_NO_FREE_STORE;
Xerror:
X    if (fl)
X	MSUnLock(fl);
X    return DOSFALSE;
X#endif
X}
X
X/*
X * Rename a file or directory, possibly moving it to a different
X * directory.
X *
X * "Tuned" to also work in full directories by first deleting the source
X * name, then look for a slot to put the destination name. If that fails,
X * we undo the deletion. By playing with the cache, we even avoid a write
X * of the sector with the undeleted entry.
X */
X
Xlong
XMSRename(slock, sname, dlock, dname)
Xstruct MSFileLock *slock;
Xbyte	       *sname;
Xstruct MSFileLock *dlock;
Xbyte	       *dname;
X{
X#ifdef READONLY
X    return DOSFALSE;
X#else
X    struct MSFileLock *sfl;
X    struct MSFileLock *dfl;
X    long	    success;
X    struct CacheSec *scache;
X    ulong	    oldstatus;
X
X    success = DOSFALSE;
X    scache = NULL;
X    dfl = NULL;
X
X    sfl = MSLock(slock, sname, SHARED_LOCK);
X    if (sfl == NULL || sfl == RootLock)
X	goto error;
X
X    /*
X     * Now we are going to pull a dirty trick with the cache. We are going
X     * to temporarily delete the source file, in the chache only, and
X     * undelete it again if we cannot create the new name. And above all
X     * we want to avoid unnecessary writes if we decide not to do the
X     * deletion after all.
X     */
X    {
X	byte	       *sec;
X	byte		old;
X
X	if ((sec = GetSec(sfl->msfl_DirSector)) == NULL)
X	    goto error;
X	scache = FindSecByBuffer(sec);
X	oldstatus = scache->sec_Refcount;
X
X	old = sfl->msfl_Msd.msd_Name[0];
X	sfl->msfl_Msd.msd_Name[0] = DIR_DELETED;
X	WriteFileLock(sfl);
X	sfl->msfl_Msd.msd_Name[0] = old;
X
X	/*
X	 * Don't FreeSec it yet; we don't want it written out to disk.
X	 */
X    }
X
X    /*
X     * Now we have freed the directory entry of the source name, we might
X     * be able to use it for the destination name. But only if we also
X     * temporarily hide the MSFileLock on that spot. Gross hack ahead!
X     */
X
X    sfl->msfl_DirOffset = ~sfl->msfl_DirOffset;
X    dfl = MSLock(dlock, dname, EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
X    sfl->msfl_DirOffset = ~sfl->msfl_DirOffset;
X
X    if (dfl != NULL || error == ERROR_OBJECT_IN_USE) {
X	error = ERROR_OBJECT_EXISTS;
X	goto undelete;
X    }
X    dfl = EmptyFileLock;
X    EmptyFileLock = NULL;
X    if (dfl == NULL) {
X	/*
X	 * Sigh, we could not create the new name. But because of that, we
X	 * are sure that we need to write nothing to the disk at all. So
X	 * we can safely reset the sector-dirty flag to what it was
X	 * before, if we also restore the cached sector.
X	 */
Xundelete:
X	WriteFileLock(sfl);
X	scache->sec_Refcount = oldstatus;
X	goto error;
X    }
X    /*
X     * Now, if the moved entry was a directory, and it was moved to a
X     * different directory, we need to adapt its "..", which is the second
X     * entry.
X     */
X
X    if (sfl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY &&
X	sfl->msfl_Parent != dfl->msfl_Parent) {
X	struct MSFileLock *parentdir;
X	struct MsDirEntry *dir;
X
X	if (dir = (struct MsDirEntry *)
X	    GetSec(DirClusterToSector(sfl->msfl_Msd.msd_Cluster))) {
X	    parentdir = MSParentDir(dfl);
X	    /*
X	     * Copy everything except the name which must remain "..". But
X	     * first a quick consistency check...
X	     */
X	    debug(("Creating new \"..\"  "));
X	    if (dir[1].msd_Name[1] == '.') {
X		CopyMem(&parentdir->msfl_Msd.msd_Attributes,
X			&dir[1].msd_Attributes,
X			(long) sizeof (struct MsDirEntry) -
X			OFFSETOF(MsDirEntry, msd_Attributes));
X		dir[1].msd_Attributes = ATTR_DIRECTORY;
X		OtherEndianMsd(&dir[1]);
X		MarkSecDirty(dir);
X	    }
X#ifdef HDEBUG
X	    else
X		debug(("!!! No \"..\" found ??\n"));
X#endif
X	    MSUnLock(parentdir);
X	    FreeSec(dir);
X	}
X    }
X    /*
X     * Move the name from the new entry to the old filelock. We do this
X     * for the case that somebody else has a lock on the (possibly moved)
X     * file/directory. Also move the other administration.
X     */
X
X    strncpy(sfl->msfl_Msd.msd_Name, dfl->msfl_Msd.msd_Name, 8 + 3);
X    sfl->msfl_DirSector = dfl->msfl_DirSector;
X    sfl->msfl_DirOffset = dfl->msfl_DirOffset;
X    /*
X     * Free the old, and get the new parent directory. They might be the
X     * same, of course...
X     */
X    MSUnLock(sfl->msfl_Parent);
X    sfl->msfl_Parent = dfl->msfl_Parent;
X    dfl->msfl_Parent = NULL;
X    sfl->msfl_Msd.msd_Attributes |= ATTR_ARCHIVED;
X    WriteFileLock(sfl);         /* Write the new name; the old name
X				 * already has been deleted. */
X    success = DOSTRUE;
X
Xerror:
X    if (sfl)
X	MSUnLock(sfl);
X    if (dfl)
X	MSUnLock(dfl);
X    if (scache)
X	FreeSec(scache->sec_Data);
X
X    return success;
X#endif
X}
END_OF_FILE
if test 19853 -ne `wc -c <'src/hanfile.c'`; then
    echo shar: \"'src/hanfile.c'\" unpacked with wrong size!
fi
# end of 'src/hanfile.c'
fi
echo shar: End of archive 3 \(of 6\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
Mail comments to the moderator at <amiga-request@cs.odu.edu>.
Post requests for sources, and general discussion to comp.sys.amiga.