[comp.sources.amiga] v90i216: MSH 1.30 - Messydos File System Handler, Part04/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 216
Archive-name: devices/msh-1.30/part04

#!/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 4 (of 6)."
# Contents:  src/hansec.c src/pack.c
# Wrapped by tadguy@xanth on Sun Jul 15 19:59:09 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/hansec.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/hansec.c'\"
else
echo shar: Extracting \"'src/hansec.c'\" \(20964 characters\)
sed "s/^X//" >'src/hansec.c' <<'END_OF_FILE'
X/*-
X * $Id: hansec.c,v 1.30 90/06/04 23:17:02 Rhialto Rel $
X * $Log:	hansec.c,v $
X * Revision 1.30  90/06/04  23:17:02  Rhialto
X * Release 1 Patch 3
X * 
X * HANSEC.C
X *
X * The code for the messydos file system handler.
X *
X * Sector-level stuff: read, write, cache, unit conversion.
X * Other interactions (via MyDoIO) with messydisk.device.
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/*#undef HDEBUG 			 /**/
X#ifdef HDEBUG
X#   define	debug(x)  dbprintf x
X#else
X#   define	debug(x)
X#endif
X
Xstruct MsgPort *DiskReplyPort;
Xstruct IOExtTD *DiskIOReq;
Xstruct IOStdReq *DiskChangeReq;
X
Xstruct DiskParam Disk;
Xbyte	       *Fat;
Xshort		FatDirty;	/* Fat must be written to disk */
X
Xshort		error;		/* To put the error value; for Result2 */
Xlong		IDDiskState;	/* InfoData.id_DiskState */
Xlong		IDDiskType;	/* InfoData.id_DiskType */
Xstruct timerequest *TimeIOReq;	/* For motor-off delay */
Xstruct MinList	CacheList;	/* Sector cache */
Xint		CurrentCache;	/* How many cached buffers do we have */
Xint		MaxCache;	/* Maximum amount of cached buffers */
Xlong		CacheBlockSize; /* Size of disk block + overhead */
Xulong		BufMemType;
Xint		DelayState;
Xshort		CheckBootBlock; /* Do we need to check the bootblock? */
X
Xword
XGet8086Word(Word8086)
Xregister byte  *Word8086;
X{
X    return Word8086[0] | Word8086[1] << 8;
X}
X
Xword
XOtherEndianWord(oew)
Xword		oew;
X{
X/* INDENT OFF */
X#asm
X	move.w	8(a5),d0
X	rol.w	#8,d0
X#endasm
X    /* INDENT ON */
X    /*
X     * return (oew << 8) | ((oew >> 8) & 0xff);
X     */
X}
X
Xulong
XOtherEndianLong(oel)
Xulong		oel;
X{
X/* INDENT OFF */
X#asm
X	move.l	8(a5),d0
X	rol.w	#8,d0
X	swap	d0
X	rol.w	#8,d0
X#endasm
X    /* INDENT ON */
X    /*
X     * return ((oel &       0xff) << 24) | ((oel &     0xff00) <<  8) |
X     * ((oel &   0xff0000) >>  8) | ((oel & 0xff000000) >> 24);
X     */
X}
X
Xvoid
XOtherEndianMsd(msd)
Xregister struct MsDirEntry *msd;
X{
X    msd->msd_Date = OtherEndianWord(msd->msd_Date);
X    msd->msd_Time = OtherEndianWord(msd->msd_Time);
X    msd->msd_Cluster = OtherEndianWord(msd->msd_Cluster);
X    msd->msd_Filesize = OtherEndianLong(msd->msd_Filesize);
X}
X
Xword
XClusterToSector(cluster)
Xregister word	cluster;
X{
X    return cluster ? Disk.start + cluster * Disk.spc
X	: 0;
X}
X
Xword
XClusterOffsetToSector(cluster, offset)
Xregister word	cluster;
Xregister word	offset;
X{
X    return cluster ? Disk.start + cluster * Disk.spc + offset / Disk.bps
X	: 0;
X}
X
Xword
XDirClusterToSector(cluster)
Xregister word	cluster;
X{
X    return cluster ? Disk.start + cluster * Disk.spc
X	: Disk.rootdir;
X}
X
Xword
XSectorToCluster(sector)
Xregister word	sector;
X{
X    return sector ? (sector - Disk.start) / Disk.spc
X	: 0;
X}
X
X/*
X * Get the next cluster in a chain. Sort-of checks for special entries.
X */
X
Xword
XNextCluster(cluster)
Xword cluster;
X{
X    register word entry;
X
X    return (entry = GetFatEntry(cluster)) >= 0xFFF7 ? FAT_EOF : entry;
X}
X
Xword
XNextClusteredSector(sector)
Xword		sector;
X{
X    word	    next = (sector + 1 - Disk.start) % Disk.spc;
X
X    if (next == 0) {
X	next = NextCluster(SectorToCluster(sector));
X	return next != FAT_EOF ? ClusterToSector(next)
X	    : SEC_EOF;
X    } else
X	return sector + 1;
X}
X
X#ifndef READONLY
X
Xword
XFindFreeSector(prev)
Xword		prev;
X{
X    word freecluster = FindFreeCluster(SectorToCluster(prev));
X
X    return freecluster == FAT_EOF ? SEC_EOF : ClusterToSector(freecluster);
X}
X
X#endif
X
X/*
X * Find a specific sector. The cache list is a Least Recently Used stack:
X * Put it on the head of the cache list. So if it is not used anymore in a
X * long time, it bubbles to the end of the list, getting a higher chance
X * of being trashed for re-use.
X */
X
Xstruct CacheSec *
XFindSecByNumber(number)
Xregister int	number;
X{
X    register struct CacheSec *sec;
X    register struct CacheSec *nextsec;
X
X    debug(("FindSecByNumber %d", number));
X
X    for (sec = (void *) CacheList.mlh_Head;
X	 nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
X	if (sec->sec_Number == number) {
X	    debug((" (%x) %lx\n", sec->sec_Refcount, sec));
X	    Remove(sec);
X	    AddHead(&CacheList, &sec->sec_Node);
X	    return sec;
X	}
X    }
X
X    debug(("; "));
X    return NULL;
X}
X
Xstruct CacheSec *
XFindSecByBuffer(buffer)
Xbyte	       *buffer;
X{
X    return (struct CacheSec *) (buffer - OFFSETOF(CacheSec, sec_Data));
X}
X
X/*
X * Get a fresh cache buffer. If we are allowed more cache, we just
X * allocate memory. Otherwise, we try to find a currently unused buffer.
X * We start looking at the end of the list, which is the bottom of the LRU
X * stack. If that fails, allocate more memory anyway. Not that is likely
X * anyway, since we currently lock only one sector at a time.
X */
X
Xstruct CacheSec *
XNewCacheSector()
X{
X    register struct CacheSec *sec;
X    register struct CacheSec *nextsec;
X
X    debug(("NewCacheSector\n"));
X
X    if (CurrentCache < MaxCache) {
X	if (sec = AllocMem(CacheBlockSize, BufMemType)) {
X	    goto add;
X	}
X    }
X    for (sec = (void *) CacheList.mlh_TailPred;
X	 nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) {
X	if ((CurrentCache >= MaxCache) && (sec->sec_Refcount == SEC_DIRTY)) {
X	    FreeCacheSector(sec);       /* Also writes it to disk */
X	    continue;
X	}
X	if (sec->sec_Refcount == 0)     /* Implies not SEC_DIRTY */
X	    return sec;
X    }
X
X    sec = AllocMem(CacheBlockSize, BufMemType);
X
X    if (sec) {
Xadd:
X	CurrentCache++;
X	AddHead(&CacheList, &sec->sec_Node);
X    } else
X	error = ERROR_NO_FREE_STORE;
X
X    return sec;
X}
X
X/*
X * Dispose a cached sector, even if it has a non-zero refcount. If it is
X * dirty, write it out.
X */
X
Xvoid
XFreeCacheSector(sec)
Xregister struct CacheSec *sec;
X{
X    debug(("FreeCacheSector %d\n", sec->sec_Number));
X    Remove(sec);
X#ifndef READONLY
X    if (sec->sec_Refcount & SEC_DIRTY) {
X	PutSec(sec->sec_Number, sec->sec_Data);
X    }
X#endif
X    FreeMem(sec, CacheBlockSize);
X    CurrentCache--;
X}
X
X/*
X * Create an empty cache list
X */
X
Xvoid
XInitCacheList()
X{
X    extern struct CacheSec *sec;    /* Of course this does not exist... */
X
X    NewList(&CacheList);
X    CurrentCache = 0;
X    CacheBlockSize = Disk.bps + sizeof (*sec) - sizeof (sec->sec_Data);
X}
X
X/*
X * Dispose all cached sectors, possibly writing them to disk.
X */
X
Xvoid
XFreeCacheList()
X{
X    register struct CacheSec *sec;
X
X    debug(("FreeCacheList, %d\n", CurrentCache));
X    while (sec = GetHead(&CacheList)) {
X	FreeCacheSector(sec);
X    }
X}
X
X/*
X * Do an insertion sort on tosort in the CacheList. Since it changes the
X * location in the list, you must fetch it before calling this routine.
X * The list will become ascending.
X */
X
Xvoid
XSortSec(tosort)
Xregister struct CacheSec *tosort;
X{
X    register struct CacheSec *sec;
X    struct CacheSec *nextsec;
X    register word   secno;
X
X    secno = tosort->sec_Number;
X    debug(("SortSec %d: ", secno));
X
X    for (sec = (void *) CacheList.mlh_Head;
X	 nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
X	debug(("%d, ", sec->sec_Number));
X	if (sec == tosort) {
X	    debug(("\n"));
X	    return;			/* No need to move it away */
X	}
X	if (sec->sec_Number > secno)
X	    break;
X    }
X    /* Insert before sec */
X    Remove(tosort);
X    Insert(&CacheList, tosort, sec->sec_Node.mln_Pred);
X    debug(("\n"));
X}
X
X/*
X * Write all dirty cache buffers to disk. They are written from highest to
X * lowest, and then the FAT is written out.
X */
X
Xvoid
XMSUpdate(immediate)
Xint		immediate;
X{
X    register struct CacheSec *sec;
X    register struct CacheSec *nextsec;
X
X    debug(("MSUpdate\n"));
X
X#ifndef READONLY
X    if (DelayState & DELAY_DIRTY) {
X	/*
X	 * First sort all dirty sectors on block number
X	 */
X	for (sec = (void *) CacheList.mlh_Head;
X	     nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
X	    if (sec->sec_Refcount & SEC_DIRTY) {
X		SortSec(sec);
X	    }
X	}
X	/*
X	 * Then do a second (backward) scan to write them out.
X	 */
X	for (sec = (void *) CacheList.mlh_TailPred;
X	     nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) {
X	    if (sec->sec_Refcount & SEC_DIRTY) {
X		PutSec(sec->sec_Number, sec->sec_Data);
X		sec->sec_Refcount &= ~SEC_DIRTY;
X	    }
X	}
X	DelayState &= ~DELAY_DIRTY;
X    }
X    if (FatDirty) {
X	WriteFat();
X    }
X#endif
X
X    if (immediate)
X	DelayState = DELAY_RUNNING1;
X
X    if (DelayState & DELAY_RUNNING2) {
X	StartTimer();
X	DelayState &= ~DELAY_RUNNING2;
X    } else {			/* DELAY_RUNNING1 */
X#ifndef READONLY
X	while (TDUpdate() != 0 && RetryRwError(DiskIOReq))
X	    ;
X#endif
X	TDMotorOff();
X	DelayState = DELAY_OFF;
X    }
X}
X
X/*
X * Start the timer which triggers cache writing and stopping the disk
X * motor.
X */
X
Xvoid
XStartTimer()
X{
X    DelayState |= DELAY_RUNNING1 | DELAY_RUNNING2;
X
X    if (CheckIO(TimeIOReq)) {
X	WaitIO(TimeIOReq);
X	TimeIOReq->tr_node.io_Command = TR_ADDREQUEST;
X	TimeIOReq->tr_time.tv_secs = 3;
X	TimeIOReq->tr_time.tv_micro = 0;
X	SendIO(TimeIOReq);
X    }
X}
X
X/*
X * Get a pointer to a logical sector { 0, ..., MS_SECTCNT - 1}. We
X * allocate a buffer and copy the data in, and lock the buffer until
X * FreeSec() is called.
X */
X
Xbyte	       *
XGetSec(sector)
Xint		sector;
X{
X    struct CacheSec *sec;
X
X#ifdef HDEBUG
X    if (sector == 0) {
X	debug(("************ GetSec(0) ***************\n"));
X    }
X#endif
X
X    if (sec = FindSecByNumber(sector)) {
X	sec->sec_Refcount++;
X
X	return sec->sec_Data;
X    }
X    if (sec = NewCacheSector()) {
X	register struct IOExtTD *req;
X
X	sec->sec_Number = sector;
X	sec->sec_Refcount = 1;
X
X	debug(("GetSec %d\n", sector));
X
X	req = DiskIOReq;
X	do {
X	    req->iotd_Req.io_Command = ETD_READ;
X	    req->iotd_Req.io_Data = (APTR)sec->sec_Data;
X	    req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps;
X	    req->iotd_Req.io_Length = Disk.bps;
X	    MyDoIO(req);
X	} while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
X
X	StartTimer();
X
X	if (req->iotd_Req.io_Error == 0) {
X	    return sec->sec_Data;
X	}
X	error = ERROR_NOT_A_DOS_DISK;
X	FreeCacheSector(sec);
X    }
X    return NULL;
X}
X
X#ifndef READONLY
X
Xbyte	       *
XEmptySec(sector)
Xint		sector;
X{
X    byte	   *buffer;
X    register struct CacheSec *sec;
X
X#ifdef HDEBUG
X    if (sector == 0) {
X	debug(("************ EmptySec(0) ***************\n"));
X    }
X#endif
X    if (sec = FindSecByNumber(sector)) {
X	sec->sec_Refcount++;
X
X	return sec->sec_Data;
X    }
X    if (sec = NewCacheSector()) {
X	sec->sec_Number = sector;
X	sec->sec_Refcount = 1;
X
X	return sec->sec_Data;
X    }
X
X    return NULL;
X}
X
Xvoid
XPutSec(sector, data)
Xint		sector;
Xbyte	       *data;
X{
X    register struct IOExtTD *req;
X
X    debug(("PutSec %d\n", sector));
X
X    req = DiskIOReq;
X    do {
X	req->iotd_Req.io_Command = ETD_WRITE;
X	req->iotd_Req.io_Data = (APTR) data;
X	req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps;
X	req->iotd_Req.io_Length = Disk.bps;
X	MyDoIO(req);
X    } while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
X
X    StartTimer();
X}
X
X#endif
X
X/*
X * Unlock a cached sector. When the usage count drops to zero, which
X * implies it is not dirty, and we are over our cache quota, the sector is
X * freed. Otherwise we keep it for re-use.
X */
X
Xvoid
XFreeSec(buffer)
Xbyte	       *buffer;
X{
X    register struct CacheSec *sec;
X
X    if (sec = FindSecByBuffer(buffer)) {
X#ifdef HDEBUG
X	if (sec->sec_Number == 0) {
X	    debug(("************ FreeSec(0) ***************\n"));
X	}
X#endif
X	if (--sec->sec_Refcount == 0) { /* Implies not SEC_DIRTY */
X	    if (CurrentCache > MaxCache) {
X		FreeCacheSector(sec);
X	    }
X	}
X    }
X}
X
X#ifndef READONLY
X
Xvoid
XMarkSecDirty(buffer)
Xbyte	       *buffer;
X{
X    register struct CacheSec *sec;
X
X    if (sec = FindSecByBuffer(buffer)) {
X	sec->sec_Refcount |= SEC_DIRTY;
X	DelayState |= DELAY_DIRTY;
X	StartTimer();
X    }
X}
X
X/*
X * Write out the FAT. Called from MSUpdate(), so don't call it again from
X * here. Don't use precious cache space for it; you could say it has its
X * own private cache already.
X */
X
Xvoid
XWriteFat()
X{
X    register int    fat,
X		    sec;
X    int 	    disksec = Disk.res;      /* First FAT, first sector */
X
X    /* Write all FATs */
X    for (fat = 0; fat < Disk.nfats; fat++) {
X	for (sec = 0; sec < Disk.spf; sec++) {
X	    PutSec(disksec++, Fat + sec * Disk.bps);
X	    /* return;	       /* Fat STILL dirty! */
X	}
X    }
X    FatDirty = FALSE;
X}
X
X#endif
X
Xint
XAwaitDFx()
X{
X    debug(("AwaitDFx\n"));
X    if (DosType) {
X	static char	dfx[] = "DFx:";
X	void	       *dfxProc,
X		       *DeviceProc();
X	char		xinfodata[sizeof(struct InfoData) + 3];
X	struct InfoData *infoData;
X	int		triesleft;
X
X	dfx[2] = '0' + UnitNr;
X	infoData = (struct InfoData *)(((long)&xinfodata[3]) & ~3L);
X
X	for (triesleft = 10; triesleft; triesleft--) {
X	    debug(("AwaitDFx %d\n", triesleft));
X	    if ((dfxProc = DeviceProc(dfx)) == NULL)
X		break;
X
X	    dos_packet(dfxProc, ACTION_DISK_INFO, CTOB(infoData));
X	    debug(("AwaitDFx %lx\n", infoData->id_DiskType));
X	    if (infoData->id_DiskType == ID_NO_DISK_PRESENT) {
X		/* DFx has not noticed yet. Wait a bit. */
X		WaitIO(TimeIOReq);
X		TimeIOReq->tr_node.io_Command = TR_ADDREQUEST;
X		TimeIOReq->tr_time.tv_secs = 0;
X		TimeIOReq->tr_time.tv_micro = 750000L;	/* .75 s */
X		SendIO(TimeIOReq);
X		continue;
X	    }
X	    if (infoData->id_DiskType == ID_DOS_DISK) {
X		/* DFx: understands it, so it is not for us. */
X		return 1;
X	    }
X	    /*
X	     * All (well, most) other values mean that DFx: does not
X	     * understand it, so we can give it a try.
X	     */
X	    break;
X	}
X    }
X    return 0;
X}
X
Xint
XReadBootBlock()
X{
X    int protstatus;
X
X    debug(("ReadBootBlock\n"));
X    FreeFat();                  /* before disk parameters change */
X    TDClear();
X
X    if (TDProtStatus() >= 0) {
X	register byte *bootblock;
X	short	    oldCancel;
X
X	oldCancel = Cancel;
X
X	if (AwaitDFx())
X	    goto bad_disk;
X	if ((protstatus = TDProtStatus()) < 0)
X	    goto no_disk;
X
X	TDChangeNum();
X	debug(("Changenumber = %ld\n", DiskIOReq->iotd_Count));
X
X	Cancel = 1;
X	if (bootblock = GetSec(0)) {
X	    word bps;
X
X	    if (CheckBootBlock &&
X				/* Atari: empty or 68000 JMP */
X		/*bootblock[0] != 0x00 && bootblock[0] != 0x4E &&*/
X				/* 8086 ml for a jump */
X		bootblock[0] != 0xE9 && bootblock[0] != 0xEB) {
X		goto bad_disk;
X	    }
X	    bps = Get8086Word(bootblock + 0x0b);
X	    Disk.spc = bootblock[0x0d];
X	    Disk.res = Get8086Word(bootblock + 0x0e);
X	    Disk.nfats = bootblock[0x10];
X	    Disk.ndirs = Get8086Word(bootblock + 0x11);
X	    Disk.nsects = Get8086Word(bootblock + 0x13);
X	    Disk.media = bootblock[0x15];
X	    Disk.spf = Get8086Word(bootblock + 0x16);
X	    Disk.spt = Get8086Word(bootblock + 0x18);
X	    Disk.nsides = Get8086Word(bootblock + 0x1a);
X	    Disk.nhid = Get8086Word(bootblock + 0x1c);
X	    FreeSec(bootblock);
X
X	    /*
X	     *	Maybe the sector size just changed. Who knows?
X	     */
X	    if (Disk.bps != bps) {
X		FreeCacheList();
X		Disk.bps = bps;
X		InitCacheList();
X	    }
X
X	    Disk.ndirsects = (Disk.ndirs * MS_DIRENTSIZE) / Disk.bps;
X	    Disk.rootdir = Disk.res + Disk.spf * Disk.nfats;
X	    Disk.datablock = Disk.rootdir + Disk.ndirsects;
X	    Disk.start = Disk.datablock - MS_FIRSTCLUST * Disk.spc;
X	    /* Available clusters are 2..maxclust in secs start..nsects-1 */
X	    Disk.maxclst = (Disk.nsects - Disk.start) / Disk.spc - 1;
X	    Disk.bpc = Disk.bps * Disk.spc;
X	    Disk.vollabel = FakeRootDirEntry;
X/*	    Disk.fat16bits = Disk.nsects > 20740;   /* DOS3.2 magic value */
X	    Disk.fat16bits = Disk.maxclst >= 0xFF7; /* DOS3.0 magic value */
X
X	    debug(("%x\tbytes per sector\n", Disk.bps));
X	    debug(("%x\tsectors per cluster\n", Disk.spc));
X	    debug(("%x\treserved blocks\n", Disk.res));
X	    debug(("%x\tfats\n", Disk.nfats));
X	    debug(("%x\tdirectory entries\n", Disk.ndirs));
X	    debug(("%x\tsectors\n", Disk.nsects));
X	    debug(("%x\tmedia byte\n", Disk.media));
X	    debug(("%x\tsectors per FAT\n", Disk.spf));
X	    debug(("%x\tsectors per track\n", Disk.spt));
X	    debug(("%x\tsides\n", Disk.nsides));
X	    debug(("%x\thidden sectors\n", Disk.nhid));
X
X	    debug(("%x\tdirectory sectors\n", Disk.ndirsects));
X	    debug(("%x\troot dir block\n", Disk.rootdir));
X	    debug(("%x\tblock for (imaginary) cluster 0\n", Disk.start));
X	    debug(("%x\tfirst data block\n", Disk.datablock));
X	    debug(("%x\tclusters total\n", Disk.maxclst));
X	    debug(("%x\tbytes per cluster\n", Disk.bpc));
X	    debug(("%x\t16-bits FAT?\n", Disk.fat16bits));
X
X	    IDDiskType = ID_DOS_DISK;
X#ifdef READONLY
X	    IDDiskState = ID_WRITE_PROTECTED;
X#else
X	    if (protstatus > 0)
X		IDDiskState = ID_WRITE_PROTECTED;
X	    else
X		IDDiskState = ID_VALIDATED;
X#endif
X
X	    if (Disk.nsects / (MS_SPT * Disk.nsides) <= 40)
X		DiskIOReq->iotd_Req.io_Flags |= IOMDF_40TRACKS;
X	    else
X		DiskIOReq->iotd_Req.io_Flags &= ~IOMDF_40TRACKS;
X
X	    GetFat();
X	} else {
X	    debug(("Can't read %d.\n", DiskIOReq->iotd_Req.io_Error));
X	bad_disk:
X	    FreeCacheList();
X	    error = ERROR_NO_DISK;
X	    IDDiskType = ID_UNREADABLE_DISK;
X	    IDDiskState = ID_WRITE_PROTECTED;
X	}
X	Cancel = oldCancel;
X    }
X#ifdef HDEBUG
X    else debug(("No disk inserted %d.\n", DiskIOReq->iotd_Req.io_Error));
X#endif
Xno_disk:
X    return 1;
X}
X
X/*
X * We try to identify the disk currently in the drive, trying to find the
X * volume label in the first directory block.
X */
X
Xint
XIdentifyDisk(name, date)
Xchar	       *name;		/* Should be at least 32 characters */
Xstruct DateStamp *date;
X{
X    debug(("IdentifyDisk\n"));
X    ReadBootBlock();            /* Also sets default vollabel */
X
X    if (IDDiskType == ID_DOS_DISK) {
X	byte	       *dirblock;
X	register struct MsDirEntry *dirent;
X
X	if (dirblock = GetSec(Disk.rootdir)) {
X	    dirent = (struct MsDirEntry *) dirblock;
X
X	    while ((byte *) dirent < &dirblock[Disk.bps]) {
X		if (dirent->msd_Attributes & ATTR_VOLUMELABEL) {
X		    Disk.vollabel.de_Msd = *dirent;
X		    Disk.vollabel.de_Sector = Disk.rootdir;
X		    Disk.vollabel.de_Offset = (byte *) dirent - dirblock;
X		    OtherEndianMsd(&Disk.vollabel.de_Msd);
X		    Disk.vollabel.de_Msd.msd_Cluster = 0;	/* to be sure */
X		    break;
X		}
X		dirent++;
X	    }
X	    strncpy(name, Disk.vollabel.de_Msd.msd_Name, 8 + 3);
X	    name[8 + 3] = '\0';
X	    ZapSpaces(name, name + 8 + 3);
X	    ToDateStamp(date, Disk.vollabel.de_Msd.msd_Date,
X			Disk.vollabel.de_Msd.msd_Time);
X	    debug(("Disk is called '%s'\n", name));
X
X	    FreeSec(dirblock);
X
X	    return 0;
X	}
X    }
X    return 1;
X}
X
X/*
X * Remove the disk change SoftInt. The V1.2 / V1.3 version is broken, so
X * we use a workaround. The correct thing to do is shown but not used.
X */
X
Xvoid
XTDRemChangeInt()
X{
X    if (DiskChangeReq) {
X	register struct IOExtTD *req = DiskIOReq;
X
X#if 0				/* V1.2 and V1.3 have a broken
X				 * TD_REMCHANGEINT */
X	req->iotd_Req.io_Command = TD_REMCHANGEINT;
X	req->iotd_Req.io_Data = (void *) DiskChangeReq;
X	MyDoIO(req);
X	WaitIO(DiskChangeReq);
X#else
X	Forbid();
X	Remove(DiskChangeReq);
X	Permit();
X#endif
X	DeleteExtIO(DiskChangeReq);
X	DiskChangeReq = NULL;
X    }
X}
X
X/*
X * Set the disk change SoftInt. Return nonzero on failure.
X */
X
Xint
XTDAddChangeInt(interrupt)
Xstruct Interrupt *interrupt;
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    if (DiskChangeReq) {
X	TDRemChangeInt();
X    }
X    DiskChangeReq = (void *)CreateExtIO(DiskReplyPort,
X					 (long) sizeof (*DiskChangeReq));
X    if (DiskChangeReq) {
X	/* Clone IO request part */
X	DiskChangeReq->io_Device = req->iotd_Req.io_Device;
X	DiskChangeReq->io_Unit = req->iotd_Req.io_Unit;
X	DiskChangeReq->io_Command = TD_ADDCHANGEINT;
X	DiskChangeReq->io_Data = (void *) interrupt;
X	SendIO(DiskChangeReq);
X
X	return 0;
X    }
X    return 1;
X}
X
X/*
X * Get the current disk change number. Necessary for ETD_ commands. Makes
X * absolutely sure nobody can change the disk without us noticing it.
X */
X
Xint
XTDChangeNum()
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    req->iotd_Req.io_Command = TD_CHANGENUM;
X    MyDoIO(req);
X    req->iotd_Count = req->iotd_Req.io_Actual;
X
X    return req->iotd_Req.io_Actual;
X}
X
X/*
X * Get the current write protection state.
X *
X * Zero means writable, one means write protected, minus one means
X * no disk in drive.
X */
X
Xint
XTDProtStatus()
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    req->iotd_Req.io_Command = TD_PROTSTATUS;
X    MyDoIO(req);
X
X    if (req->iotd_Req.io_Error)
X	return -1;
X
X    return req->iotd_Req.io_Actual != 0;
X}
X
X/*
X * Switch the drive motor off. Return previous state. Don't use this when
X * you have allocated the disk via GetDrive().
X */
X
Xint
XTDMotorOff()
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    req->iotd_Req.io_Command = TD_MOTOR;
X    req->iotd_Req.io_Length = 0;
X    MyDoIO(req);
X
X    return req->iotd_Req.io_Actual;
X}
X
X/*
X * Clear all internal messydisk buffers.
X */
X
Xint
XTDClear()
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    req->iotd_Req.io_Command = CMD_CLEAR;
X
X    return MyDoIO(req);
X}
X
X#ifndef READONLY
X/*
X * Write out all internal messydisk buffers to the disk.
X */
X
Xint
XTDUpdate()
X{
X    register struct IOExtTD *req = DiskIOReq;
X
X    req->iotd_Req.io_Command = ETD_UPDATE;
X
X    return MyDoIO(req);
X}
X#endif
X
Xint
XMyDoIO(ioreq)
Xregister struct IOStdReq *ioreq;
X{
X    ioreq->io_Flags |= IOF_QUICK;	/* Preserve IOMDF_40TRACKS */
X    BeginIO(ioreq);
X    return WaitIO(ioreq);
X}
END_OF_FILE
if test 20964 -ne `wc -c <'src/hansec.c'`; then
    echo shar: \"'src/hansec.c'\" unpacked with wrong size!
fi
# end of 'src/hansec.c'
fi
if test -f 'src/pack.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/pack.c'\"
else
echo shar: Extracting \"'src/pack.c'\" \(26338 characters\)
sed "s/^X//" >'src/pack.c' <<'END_OF_FILE'
X/*-
X * $Id: pack.c,v 1.30 90/06/04 23:15:58 Rhialto Rel $
X * $Log:	pack.c,v $
X * Revision 1.30  90/06/04  23:15:58  Rhialto
X * Release 1 Patch 3
X * 
X *  Originally:
X *
X *	DOSDEVICE.C	    V1.10   2 November 1987
X *
X *	EXAMPLE DOS DEVICE DRIVER FOR AZTEC.C	PUBLIC DOMAIN.
X *
X *	By Matthew Dillon.
X *
X *  This has been stripped and refilled with messydos code
X *  by Olaf Seibert.
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 *  Please note that we are NOT pure, so if you wish to mount
X *  multiple MSDOS units, you must use different copies of this driver.
X *
X *  This file forms the interface between the actual handler code and all
X *  AmigaDOS requirements. It shields it from ugly stuff like BPTRs, BSTRs,
X *  FileLocks, FileHandles and VolumeNodes (in the form of DeviceLists).
X *  Also, most protection against non-inserted disks is done here.
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
X/*
X * Since this code might be called several times in a row without being
X * unloaded, you CANNOT ASSUME GLOBALS HAVE BEEN ZERO'D!!  This also goes
X * for any global/static assignments that might be changed by running the
X * code.
X */
X
XPORT	       *DosPort;	/* Our DOS port... */
XDEVNODE        *DevNode;	/* Our DOS node.. created by DOS for us */
XDEVLIST        *VolNode;	/* Device List structure for our volume
X				 * node */
X
Xvoid	       *SysBase;	/* EXEC library base */
XDOSLIB	       *DOSBase;	/* DOS library base for debug process */
Xlong		PortMask;	/* The signal mask for our DosPort */
Xlong		WaitMask;	/* The signal mask to wait for */
Xshort		DiskChanged;	/* Set by disk change interrupt */
Xshort		Inhibited;	/* Are we inhibited (ACTION_INHIBIT)? */
Xlong		UnitNr; 	/* From */
Xchar	       *DevName;	/*   the */
Xulong		DevFlags;	/*     mountlist */
Xlong		DosType;
XPACKET	       *DosPacket;	/* For the SystemRequest pr_WindowPtr */
X
Xvoid ChangeIntHand(), DiskChange();
Xvoid NewVolNodeName(), NewVolNodeDate();
X
Xstruct Interrupt ChangeInt = {
X    { 0 },			/* is_Node */
X    0,				/* is_Data */
X    ChangeIntHand,		/* is_Code */
X};
X
X/*
X * Don't call the entry point main().  This way, if you make a mistake
X * with the compile options you'll get a link error.
X */
X
Xvoid
Xmessydoshandler()
X{
X    register PACKET *packet;
X    MSG 	   *msg;
X    byte	    notdone;
X    long	    OpenCount;	    /* How many open files/locks there are */
X
X    /*
X     * Initialize all global variables.  SysBase MUST be initialized
X     * before we can make Exec calls.  AbsExecBase is a library symbol
X     * referencing absolute memory location 4.
X     */
X
X    SysBase = AbsExecBase;
X    DOSBase = OpenLibrary("dos.library", 0L);
X
X#ifdef HDEBUG
X    /*
X     * Initialize debugging code as soon as possible. Only SysBase and
X     * DOSBase are required.
X     */
X
X    dbinit();
X#endif				/* HDEBUG */
X
X    DosPort = &((struct Process *)FindTask(NULL))->pr_MsgPort;
X
X    WaitPort(DosPort);      /* Get Startup Packet  */
X    msg = GetMsg(DosPort);
X    packet = (PACKET *) msg->mn_Node.ln_Name;
X
X
X    DevNode = BTOC(PArg3);
X    {
X	struct FileSysStartupMsg *fssm;
X	ulong *environ;
X	ulong Reserved;
X
X	DevName = "messydisk.device";
X	UnitNr = 0;
X	DevFlags = 0;
X
X	MaxCache = 5;
X	BufMemType = MEMF_PUBLIC;
X	Disk.nsides = MS_NSIDES;
X	Disk.spt = MS_SPT;
X	Disk.bps = MS_BPS;
X	Disk.lowcyl = 0;
X	Reserved = 0;
X
X	if (fssm = (struct FileSysStartupMsg *)BTOC(DevNode->dn_Startup)) {
X				    /* Same as BTOC(packet->dp_Arg2) */
X	    UnitNr = fssm->fssm_Unit;
X	    if (fssm->fssm_Device)
X		DevName = (char *)BTOC(fssm->fssm_Device)+1;
X	    DevFlags = fssm->fssm_Flags;
X
X	    if (environ = BTOC(fssm->fssm_Environ)) {
X		debug(("environ size %ld\n", environ[0]));
X#define get(xx,yy)  if (environ[DE_TABLESIZE] >= yy) xx = environ[yy];
X
X		get(MaxCache, DE_NUMBUFFERS);
X		get(BufMemType, DE_MEMBUFTYPE);
X		get(Disk.nsides, DE_NUMHEADS);
X		get(Disk.spt, DE_BLKSPERTRACK);
X		get(Disk.bps, DE_SIZEBLOCK);
X		Disk.bps *= 4;
X		debug(("Disk.bps %d\n", Disk.bps));
X		get(Disk.lowcyl, DE_LOWCYL);
X		get(Reserved, DE_RESERVEDBLKS);
X		get(DosType, DE_DOSTYPE);
X#undef get
X	    }
X	}
X	Disk.lowcyl *= (long)MS_BPS * Disk.spt * Disk.nsides;
X	Disk.lowcyl += (long)MS_BPS * Reserved;
X    }
X
X    if (DOSBase && HanOpenUp()) {
X	/*
X	 * Loading DevNode->dn_Task causes DOS *NOT* to startup a new
X	 * instance of the device driver for every reference.	E.G. if
X	 * you were writing a CON: device you would want this field to be
X	 * NULL.
X	 */
X
X	DevNode->dn_Task = DosPort;
X
X	PRes1 = DOSTRUE;
X	PRes2 = 0;
X    } else {		    /* couldn't open dos.library  */
X	PRes1 = DOSFALSE;
X	PRes2 = ERROR_DEVICE_NOT_MOUNTED;   /* no better message available */
X	returnpacket(packet);
X	goto exit;		/* exit process    */
X    }
X    returnpacket(packet);
X
X    /* Initialize some more global variables	*/
X
X    PortMask = 1L << DosPort->mp_SigBit;
X    VolNode = NULL;
X    OpenCount = 0;
X    Inhibited = 0;
X
X    /* Get the first real packet       */
X    WaitPort(DosPort);
X    msg = GetMsg(DosPort);
X    notdone = 1;
X    WaitMask = PortMask | (1L << DiskReplyPort->mp_SigBit);
X    TDAddChangeInt(&ChangeInt);
X    DiskInserted(WhichDiskInserted());
X
X    goto entry;
X
X    /*
X     * Here begins the endless loop, waiting for requests over our message
X     * port and executing them.  Since requests are sent over the message
X     * port in our device and volume nodes, we must not use our Process
X     * message port for this: this precludes being able to call DOS
X     * functions ourselves.
X     */
X
Xtop:
X    for (notdone = 1; notdone;) {
X	Wait(WaitMask);
X	if (DiskChanged)
X	    DiskChange();
X	while (msg = GetMsg(DosPort)) {
X	    byte	    buf[256];	/* Max length of BCPL strings is
X					 * 255 + 1 for \0. */
X
X    entry:
X	    if (DiskChanged)
X		DiskChange();
X	    packet = (PACKET *) msg->mn_Node.ln_Name;
X	    PRes1 = DOSFALSE;
X	    PRes2 = 0;
X	    error = 0;
X	    debug(("Packet: %3ld %08lx %08lx %08lx %10s\n",
X		     PType, PArg1, PArg2, PArg3, typetostr(PType)));
X
X	    DosPacket = packet; 	/* For the System Requesters */
X	    switch (PType) {
X	    case ACTION_DIE:		/* attempt to die?  */
X		notdone = 0;		/* try to die	 */
X		break;
X	    case ACTION_CURRENT_VOLUME: /* -		      VolNode,UnitNr */
X		PRes1 = (long) CTOB(VolNode);
X		PRes2 = UnitNr;
X		break;
X	    case ACTION_LOCATE_OBJECT:	/* Lock,Name,Mode	Lock	     */
X		{
X		    register struct FileLock *newlock;
X		    struct FileLock *lock;
X		    struct MSFileLock *msfl;
X		    long	    lockmode;
X
X		    lock = BTOC(PArg1);
X		    if (CheckRead(lock))
X			break;
X		    btos(PArg2, buf);
X		    if ((lockmode = PArg3) != EXCLUSIVE_LOCK)
X			lockmode = SHARED_LOCK;
X		    msfl = MSLock(lock ? lock->fl_Key : NULL,
X				  buf,
X				  lockmode);
X		    if (msfl) {
X			if (newlock = NewFileLock(msfl, lock)) {
X			    newlock->fl_Access = lockmode;
X			    PRes1 = (long) CTOB(newlock);
X			    OpenCount++;
X			} else
X			    MSUnLock(msfl);
X		    }
X		}
X		break;
X	    case ACTION_RENAME_DISK:	/* BSTR:NewName 	   Bool      */
X		if (CheckWrite(NULL))
X		    break;
X		btos(PArg1, buf);
X		buf[31] = '\0';
X		if (PRes1 = MSRelabel(buf))
X		    NewVolNodeName();
X		break;
X	    case ACTION_FREE_LOCK:	/* Lock 		   Bool      */
X		{
X		    struct FileLock *lock;
X		    struct MSFileLock *msfl;
X
X		    PRes1 = DOSTRUE;
X		    lock = BTOC(PArg1);
X		    if (lock == NULL)
X			break;
X
X		    msfl = (struct MSFileLock *)lock->fl_Key;
X		    FreeFileLock(lock); /* may remove last lock on volume */
X		    MSUnLock(msfl);     /* may call MayFreeVolNode */
X		    OpenCount--;
X		}
X		break;
X	    case ACTION_DELETE_OBJECT:	/* Lock,Name		Bool	     */
X		{
X		    struct FileLock *lock;
X
X		    lock = BTOC(PArg1);
X		    if (CheckWrite(lock))
X			break;
X		    btos(PArg2, buf);
X		    PRes1 = MSDeleteFile(lock ? lock->fl_Key : NULL,
X					 buf);
X		}
X		break;
X	    case ACTION_RENAME_OBJECT:	/* SLock,SName,DLock,DName   Bool    */
X		{
X		    struct FileLock *slock, *dlock;
X		    char	     buf2[256];
X
X		    slock = BTOC(PArg1);
X		    dlock = BTOC(PArg3);
X		    if (CheckWrite(slock) || CheckWrite(dlock))
X			break;
X		    btos(PArg2, buf);
X		    btos(PArg4, buf2);
X		    PRes1 = MSRename(slock ? slock->fl_Key : NULL,
X				     buf,
X				     dlock ? dlock->fl_Key : NULL,
X				     buf2);
X		}
X		break;
X	    case ACTION_MORECACHE:	/* #BufsToAdd		   Bool      */
X		if ((MaxCache += (short) PArg1) <= 0) {
X		    MaxCache = 1;
X		} else
X		    PRes1 = DOSTRUE;
X		debug(("Now %d cache sectors\n", MaxCache));
X		break;
X	    case ACTION_COPY_DIR:	/* Lock 		   Lock      */
X		{
X		    register struct FileLock *newlock;
X		    struct FileLock *lock;
X		    struct MSFileLock *msfl;
X
X		    lock = BTOC(PArg1);
X
X		    msfl = MSDupLock(lock ? lock->fl_Key : NULL);
X
X		    if (msfl) {
X			if (newlock = NewFileLock(msfl, lock)) {
X			    newlock->fl_Access =
X				lock ? lock->fl_Access : SHARED_LOCK;
X			    PRes1 = (long) CTOB(newlock);
X			    OpenCount++;
X			} else
X			    MSUnLock(msfl);
X		    }
X		}
X		break;
X	    case ACTION_SET_PROTECT:	/* -,Lock,Name,Mask	   Bool      */
X		{
X		    struct FileLock *lock;
X
X		    lock = BTOC(PArg2);
X		    if (CheckWrite(lock))
X			break;
X		    btos(PArg3, buf);
X		    PRes1 = MSSetProtect(lock ? lock->fl_Key : NULL, buf, PArg4);
X		}
X		break;
X	    case ACTION_CREATE_DIR:	/* Lock,Name		Lock	     */
X		{
X		    register struct FileLock *newlock;
X		    struct FileLock *lock;
X		    struct MSFileLock *msfl;
X
X		    lock = BTOC(PArg1);
X		    if (CheckWrite(lock))
X			break;
X		    btos(PArg2, buf);
X
X		    msfl = MSCreateDir(lock ? lock->fl_Key : NULL, buf);
X
X		    if (msfl) {
X			if (newlock = NewFileLock(msfl, lock)) {
X			    newlock->fl_Access = SHARED_LOCK;
X			    PRes1 = (long) CTOB(newlock);
X			    OpenCount++;
X			} else
X			    MSUnLock(msfl);
X		    }
X		}
X		break;
X	    case ACTION_EXAMINE_OBJECT: /* Lock,Fib	       Bool	     */
X		{
X		    struct FileLock *lock;
X
X		    lock = BTOC(PArg1);
X		    if (CheckRead(lock))
X			break;
X		    PRes1 = MSExamine(lock ? lock->fl_Key : NULL, BTOC(PArg2));
X		}
X		break;
X	    case ACTION_EXAMINE_NEXT:	/* Lock,Fib	       Bool	     */
X		{
X		    struct FileLock *lock;
X
X		    lock = BTOC(PArg1);
X		    if (CheckRead(lock))
X			break;
X		    PRes1 = MSExNext(lock ? lock->fl_Key : NULL, BTOC(PArg2));
X		}
X		break;
X	    case ACTION_DISK_INFO:	/* InfoData	       Bool:TRUE     */
X		PRes1 = MSDiskInfo(BTOC(PArg1));
X		break;
X	    case ACTION_INFO:	/* Lock,InfoData	       Bool:TRUE     */
X		if (CheckRead(BTOC(PArg1)))
X		    break;
X		PRes1 = MSDiskInfo(BTOC(PArg2));
X		break;
X	    case ACTION_FLUSH:		/* writeout bufs, disk motor off     */
X		MSUpdate(1);
X		break;
X/*	    case ACTION_SET_COMMENT:	/* -,Lock,Name,Comment	   Bool      */
X	    case ACTION_PARENT: /* Lock 		       ParentLock    */
X		{
X		    register struct FileLock *newlock;
X		    struct FileLock *lock;
X		    struct MSFileLock *msfl;
X		    long mode;
X
X		    lock = BTOC(PArg1);
X
X		    msfl = MSParentDir(lock ? lock->fl_Key : NULL);
X
X		    if (msfl) {
X			if (newlock = NewFileLock(msfl, lock)) {
X			    newlock->fl_Access = SHARED_LOCK;
X			    PRes1 = (long) CTOB(newlock);
X			    OpenCount++;
X			} else
X			    MSUnLock(msfl);
X		    }
X		}
X		break;
X	    case ACTION_INHIBIT:	/* Bool 		   Bool      */
X		if (Inhibited = PArg1) {
X		    DiskRemoved();
X		} else { /* Fall through to ACTION_DISK_CHANGE: */
X	    case ACTION_DISK_CHANGE:	/* ?			   ?	     */
X		    DiskChange();
X		}
X		PRes1 = DOSTRUE;
X		break;
X	    case ACTION_SET_DATE: /* -,Lock,Name,CPTRDateStamp	   Bool      */
X		{
X		    struct FileLock *lock;
X
X		    lock = BTOC(PArg2);
X		    if (CheckWrite(lock))
X			break;
X		    btos(PArg3, buf);
X		    PRes1 = MSSetDate(lock ? lock->fl_Key : NULL,
X				      buf,
X				      PArg4);
X		}
X		break;
X	    case ACTION_READ:	/* FHArg1,CPTRBuffer,Length	  ActLength  */
X		if (CheckRead(NULL)) {
X		    PRes1 = -1;
X		} else
X		    PRes1 = MSRead(PArg1, PArg2, PArg3);
X		break;
X	    case ACTION_WRITE:	/* FHArg1,CPTRBuffer,Length	  ActLength  */
X		if (CheckWrite(NULL)) {
X		    PRes1 = -1;
X		} else
X		    PRes1 = MSWrite(PArg1, PArg2, PArg3);
X		break;
X	    case ACTION_OPENNEW:	/* FileHandle,Lock,Name    Bool      */
X		{
X		    struct MSFileHandle *msfh;
X		    struct FileHandle *fh;
X		    struct FileLock *lock;
X
X		    if (CheckWrite(BTOC(PArg2)))
X			break;
X	    case ACTION_OPENRW: 	/* FileHandle,Lock,Name    Bool      */
X	    case ACTION_OPENOLD:	/* FileHandle,Lock,Name    Bool      */
X
X		    fh = BTOC(PArg1);
X		    lock = BTOC(PArg2);
X		    if (CheckRead(lock))
X			break;
X		    btos(PArg3, buf);
X		    debug(("'%s' ", buf));
X		    msfh = MSOpen(lock ? lock->fl_Key : NULL,
X				  buf,
X				  PType);
X		    if (msfh) {
X			fh->fh_Arg1 = (long) msfh;
X			PRes1 = DOSTRUE;
X			OpenCount++;
X		    }
X		}
X		break;
X	    case ACTION_CLOSE:	/* FHArg1			  Bool:TRUE  */
X		MSClose(PArg1);
X		PRes1 = DOSTRUE;
X		OpenCount--;
X		break;
X	    case ACTION_SEEK:	/* FHArg1,Position,Mode 	 OldPosition */
X		if (CheckRead(NULL)) {
X		    PRes1 = -1;
X		} else
X		    PRes1 = MSSeek(PArg1, PArg2, PArg3);
X		break;
X		/*
X		 * A few other packet types which we do not support
X		 */
X/*	    case ACTION_WAIT_CHAR:	/* Timeout, ticks	   Bool      */
X/*	    case ACTION_RAWMODE:	/* Bool(-1:RAW 0:CON)      OldState  */
X	    default:
X		error = ERROR_ACTION_NOT_KNOWN;
X		break;
X	    } /* end switch */
X	    if (packet) {
X		if (error) {
X		    debug(("ERR=%d\n", error));
X		    PRes2 = error;
X		}
X#ifdef HDEBUG
X		else {
X		    debug(("RES=%06lx\n", PRes1));
X		}
X#endif
X		returnpacket(packet);
X		DosPacket = NULL;
X	    }
X#ifdef HDEBUG
X	    else {
X		debug(("NOREP\n"));
X	    }
X#endif
X	} /* end while (GetMsg()) */
X
X	/*
X	 *  Now check for an other cause of events: timer IO.
X	 *  Unfortunately we cannot be sure that we always get a signal
X	 *  when the timeout has elapsed, since the same message port is
X	 *  used for other IO.
X	 */
X	if (CheckIO(TimeIOReq)) {   /* Timer finished? */
X	    debug(("TimeIOReq is finished\n"));
X	    if (DelayState != DELAY_OFF) {
X		MSUpdate(0);    /* Also may switch off motor */
X	    }
X	}
X    } /* end for (;notdone) */
X
X#ifdef HDEBUG
X    debug(("Can we remove ourselves? "));
X    Delay(50L);                 /* I wanna even see the debug message! */
X#endif				/* HDEBUG */
X    Forbid();
X    if (OpenCount || packetsqueued()) {
X	Permit();
X	debug((" ..  not yet!\n"));
X	goto top;		/* sorry... can't exit     */
X    }
X    debug((" .. yes!\n"));
X
X    /*
X     * Causes a new process to be created on next reference.
X     */
X
X    DevNode->dn_Task = NULL;
X    TDRemChangeInt();
X    DiskRemoved();
X    HanCloseDown();
X    debug(("HanCloseDown returned. dbuninit in 2 seconds:\n"));
X
X    /*
X     * Remove debug window, closedown, fall of the end of the world.
X     */
Xexit:
X#ifdef HDEBUG
X    Delay(100L);                /* This is dangerous! */
X    dbuninit();
X#endif				/* HDEBUG */
X
X#if 1
X    UnLoadSeg(DevNode->dn_SegList);     /* This is real fun. We are still */
X    DevNode->dn_SegList = NULL; 	/* Forbid()den, fortunately */
X#endif
X
X    CloseLibrary(DOSBase);
X
X    /* Fall off the end of the world. Implicit Permit(). */
X}
X
Xvoid
XChangeIntHand()
X{
X/* INDENT OFF */
X#asm
X    move.l  a6,-(sp)
X#endasm
X    DiskChanged = 1;
X    Signal(DosPort->mp_SigTask, PortMask);
X#asm
X    move.l  (sp)+,a6
X#endasm
X/* INDENT ON */
X}
X
X/*
X *  Make a new struct FileLock, for DOS use. It is put on a singly linked
X *  list, which is attached to the same VolumeNode the old lock was on.
X *
X *  Also note that we must ALWAYS be prepared to UnLock() or DupLock()
X *  any FileLocks we ever made, even if the volume in question has been
X *  removed and/or inserted into another drive with another FileSystem
X *  handler!
X *
X * DOS makes certain assumptions about LOCKS.	A lock must minimally be a
X * FileLock structure, with additional private information after the
X * FileLock structure.	The longword before the beginning of the structure
X * must contain the length of structure + 4.
X *
X * NOTE!!!!! The workbench does not follow the rules and assumes it can copy
X * lock structures.  This means that if you want to be workbench
X * compatible, your lock structures must be EXACTLY sizeof(struct
X * FileLock). Also, it sometimes uses uninitialized values for the lock mode...
X */
X
Xstruct FileLock *
XNewFileLock(msfl, fl)
Xstruct MSFileLock *msfl;
Xstruct FileLock *fl;
X{
X    struct FileLock *newlock;
X    DEVLIST *volnode;
X
X    if (fl) {
X	volnode = BTOC(fl->fl_Volume);
X    } else {
X	volnode = VolNode;
X    }
X
X    if (newlock = dosalloc((ulong)sizeof (*newlock))) {
X	newlock->fl_Key = (ulong) msfl;
X	newlock->fl_Task = DosPort;
X	newlock->fl_Volume = (BPTR) CTOB(volnode);
X	Forbid();
X	newlock->fl_Link = volnode->dl_LockList;
X	volnode->dl_LockList = (BPTR) CTOB(newlock);
X	Permit();
X    } else
X	error = ERROR_NO_FREE_STORE;
X
X    return newlock;
X}
X
X/*
X *  This should be called before MSUnLock(), so that it may call
X *  MayFreeVolNode() which then calls FreeVolNode(). A bit tricky,
X *  I'm sorry for that.
X */
X
Xlong
XFreeFileLock(lock)
Xstruct FileLock *lock;
X{
X    register struct FileLock *fl;
X    register struct FileLock **flp;
X    DEVLIST	   *volnode;
X
X    volnode = (DEVLIST *)BTOC(lock->fl_Volume);
X    flp = (struct FileLock **) &volnode->dl_LockList;
X    for (fl = BTOC(*flp); fl && fl != lock; fl = BTOC(fl->fl_Link))
X	flp = (struct FileLock **)&fl->fl_Link;
X
X    if (fl == lock) {
X	*(BPTR *)flp = fl->fl_Link;
X	dosfree(fl);
X	return DOSTRUE;
X    } else {
X	debug(("Huh?? Could not find filelock!\n"));
X	return DOSFALSE;
X    }
X}
X
X/*
X * Create Volume node and add to the device list.   This will
X * cause the WORKBENCH to recognize us as a disk.   If we
X * don't create a Volume node, Wb will not recognize us.
X * However, we are a MESSYDOS: disk, Volume node or not.
X */
X
XDEVLIST        *
XNewVolNode(name, date)
Xstruct DateStamp *date;
Xchar *name;
X{
X    DOSINFO	   *di;
X    register DEVLIST *volnode;
X    char	   *volname;	    /* This is my volume name */
X
X    di = BTOC(((ROOTNODE *) DOSBase->dl_Root)->rn_Info);
X
X    if (volnode = dosalloc((ulong)sizeof (DEVLIST))) {
X	if (volname = dosalloc(32L)) {
X	    volname[0] = strlen(name);
X	    strcpy(volname + 1, name);      /* Make sure \0 terminated */
X
X	    volnode->dl_Type = DLT_VOLUME;
X	    volnode->dl_Task = DosPort;
X	    volnode->dl_DiskType = IDDiskType;
X	    volnode->dl_Name = CTOB(volname);
X	    volnode->dl_VolumeDate = *date;
X	    volnode->dl_MSFileLockList = NULL;
X
X	    Forbid();
X	    volnode->dl_Next = di->di_DevInfo;
X	    di->di_DevInfo = (long) CTOB(volnode);
X	    Permit();
X	} else {
X	    dosfree(volnode);
X	    volnode = NULL;
X	}
X    } else {
X	error = ERROR_NO_FREE_STORE;
X    }
X
X    return volnode;
X}
X
X/*
X *  Get the current VolNode a new name from the volume label.
X */
X
Xvoid
XNewVolNodeName()
X{
X    if (VolNode) {
X	register char *volname = BTOC(VolNode->dl_Name);
X
X	strncpy(volname + 1, Disk.vollabel.de_Msd.msd_Name, 8+3);
X	volname[1+8+3] = '\0';      /* Make sure \0 terminated */
X	ZapSpaces(volname + 1, volname + 1 + 8+3);
X	volname[0] = strlen(volname+1);
X    }
X}
X
X/*
X *  Get the current VolNode a new date, from the last root directory.
X */
X
Xvoid
XNewVolNodeDate()
X{
X    if (VolNode) {
X	ToDateStamp(&VolNode->dl_VolumeDate,
X		    Disk.vollabel.de_Msd.msd_Date,
X		    Disk.vollabel.de_Msd.msd_Time);
X    }
X}
X
X/*
X * Remove Volume entry.  Since DOS uses singly linked lists, we must
X * (ugg) search it manually to find the link before our Volume entry.
X */
X
Xvoid
XFreeVolNode(volnode)
XDEVLIST        *volnode;
X{
X    DOSINFO	   *di = BTOC(((ROOTNODE *) DOSBase->dl_Root)->rn_Info);
X    register DEVLIST *dl;
X    register void  *dlp;
X
X    debug(("FreeVolNode %08lx\n", volnode));
X
X    if (volnode == NULL)
X	return;
X
X    dlp = &di->di_DevInfo;
X    Forbid();
X    for (dl = BTOC(di->di_DevInfo); dl && dl != volnode; dl = BTOC(dl->dl_Next))
X	dlp = &dl->dl_Next;
X    if (dl == volnode) {
X	*(BPTR *) dlp = dl->dl_Next;
X	dosfree(BTOC(dl->dl_Name));
X	dosfree(dl);
X    }
X#ifdef HDEBUG
X    else {
X	debug(("****PANIC: Unable to find volume node\n"));
X    }
X#endif				/* HDEBUG */
X    Permit();
X
X    if (volnode == VolNode)
X	VolNode = NULL;
X}
X
X/*
X *  This is also called from the real handler when the last lock on a
X *  volume is UnLock()ed, or the last file has been Close()d.
X */
X
Xint
XMayFreeVolNode(volnode)
XDEVLIST *volnode;
X{
X    if (volnode->dl_LockList == NULL) {
X	FreeVolNode(volnode);
X	return 1;
X    }
X
X    return 0;
X}
X
X/*
X *  Our disk has been removed. Save the FileLocks in the dl_LockList,
X *  and let the handler save its MSFileLocks in the dl_MSFileLockList field.
X *  If it has nothing to save, forget about the volume, and return
X *  DOSTRUE.
X *  There is one subtlety that MSDiskRemoved must know about:
X *  If it MSUnLock()s the last lock on the volume, the VolNode is
X *  deleted via FreeLockList().. MayFreeVolNode().. FreeVolNode().
X *  But then there is no place anymore to put NULL in, so that needs
X *  to be done first.
X */
X
Xint
XDiskRemoved()
X{
X    debug(("DiskRemoved %08lx\n", VolNode));
X
X    if (VolNode == NULL) {
X	IDDiskType = ID_NO_DISK_PRESENT;/* really business of MSDiskRemoved */
X	return DOSTRUE;
X    }
X
X    VolNode->dl_Task = NULL;
X    MSDiskRemoved(&VolNode->dl_MSFileLockList);
X    if (VolNode == NULL) {  /* Could happen via MSDiskRemoved() */
X	return DOSTRUE;
X    }
X    NewVolNodeDate();       /* Fetch new date of root directory */
X    VolNode = NULL;
X    return DOSFALSE;
X}
X
X/*
X *  Reconstruct everything from a Volume node
X */
X
Xvoid
XDiskInserted(volnode)
Xregister DEVLIST	*volnode;
X{
X    debug(("DiskInserted %08lx\n", volnode));
X
X    VolNode = volnode;
X
X    if (volnode) {
X	volnode->dl_Task = DosPort;
X	MSDiskInserted(&volnode->dl_MSFileLockList, volnode);
X	volnode->dl_MSFileLockList = NULL;
X    }
X}
X
XDEVLIST *
XWhichDiskInserted()
X{
X    char name[34];
X    struct DateStamp date;
X    register DEVLIST *dl = NULL;
X
X    if (!Inhibited && IdentifyDisk(name, &date) == 0) {
X	DOSINFO        *di = BTOC(((ROOTNODE *) DOSBase->dl_Root)->rn_Info);
X	byte	       *nodename;
X	int		namelen = strlen(name);
X
X	for (dl = BTOC(di->di_DevInfo); dl; dl = BTOC(dl->dl_Next)) {
X	    nodename = BTOC(dl->dl_Name);
X	    if (nodename[0] != namelen || strncmp(nodename+1,name,namelen))
X		continue;
X	    if (dl->dl_VolumeDate == date)  /* Non-standard! Structure compare! */
X		break;
X	}
X
X	name[31] = '\0';
X	if (dl == NULL)
X	    dl = NewVolNode(name, &date);
X    }
X
X    return dl;
X}
X
Xvoid
XDiskChange()
X{
X    debug(("DiskChange\n"));
X    DiskChanged = 0;
X    DiskRemoved();
X    DiskInserted(WhichDiskInserted());
X}
X
Xint
XCheckRead(lock)
Xstruct FileLock *lock;
X{
X    if (lock && BTOC(lock->fl_Volume) != VolNode)
X	error = ERROR_DEVICE_NOT_MOUNTED;
X    else if (IDDiskType == ID_NO_DISK_PRESENT)
X	error = ERROR_NO_DISK;
X    else if (IDDiskType != ID_DOS_DISK)
X	error = ERROR_NOT_A_DOS_DISK;
X
X    return error;
X}
X
Xint
XCheckWrite(lock)
Xstruct FileLock *lock;
X{
X    if (lock && BTOC(lock->fl_Volume) != VolNode)
X	error = ERROR_DEVICE_NOT_MOUNTED;
X    else if (IDDiskType == ID_NO_DISK_PRESENT)
X	error = ERROR_NO_DISK;
X    else if (IDDiskType != ID_DOS_DISK)
X	error = ERROR_NOT_A_DOS_DISK;
X    else if (IDDiskState == ID_VALIDATING)
X	error = ERROR_DISK_NOT_VALIDATED;
X    else if (IDDiskState != ID_VALIDATED)
X	error = ERROR_DISK_WRITE_PROTECTED;
X
X    return error;
X}
X
X#ifdef HDEBUG
X		    /*	DEBUGGING			*/
XPORT *Dbport;	    /*	owned by the debug process	*/
XPORT *Dback;	    /*	owned by the DOS device driver	*/
Xshort DBEnable;
X
X/*
X *  DEBUGGING CODE.	You cannot make DOS library calls that access other
X *  devices from within a DOS device driver because they use the same
X *  message port as the driver.  If you need to make such calls you must
X *  create a port and construct the DOS messages yourself.  I do not
X *  do this.  To get debugging info out another PROCESS is created to which
X *  debugging messages can be sent.
X */
X
Xextern void debugproc();
X
Xdbinit()
X{
X    TASK *task = FindTask(NULL);
X
X    Dback = CreatePort("MSH:Dback", -1L);
X    CreateProc("MSH_DB", (long)task->tc_Node.ln_Pri+1, CTOB(debugproc), 4096L);
X    WaitPort(Dback);                                /* handshake startup    */
X    GetMsg(Dback);                                  /* remove dummy msg     */
X    DBEnable = 1;
X    dbprintf("Debugger running V1.10\n");
X}
X
Xdbuninit()
X{
X    MSG killmsg;
X
X    if (Dbport) {
X	killmsg.mn_Length = 0;	    /*	0 means die	    */
X	PutMsg(Dbport,  &killmsg);
X	WaitPort(Dback);            /*  He's dead jim!      */
X	GetMsg(Dback);
X	DeletePort(Dback);
X
X	/*
X	 *  Since the debug process is running at a greater priority, I
X	 *  am pretty sure that it is guarenteed to be completely removed
X	 *  before this task gets control again.  Still, it doesn't hurt...
X	 */
X
X	Delay(50L);                 /*  ensure he's dead    */
X    }
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	MSG	msg;
X	char	buf[256];
X    } msgbuf;
X    register MSG *msg = &msgbuf.msg;
X    register long len;
X
X    if (Dbport && DBEnable) {
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	PutMsg(Dbport, msg);
X	WaitPort(Dback);
X	GetMsg(Dback);
X    }
X}
X
X/*
X *  BTW, the DOS library used by debugmain() was actually opened by
X *  the device driver.
X */
X
Xdebugmain()
X{
X    register MSG *msg;
X    register long len;
X    register void *fh;
X    void *fh2;
X    MSG DummyMsg;
X
X    Dbport = CreatePort("MSH:Dbport", -1L);
X    fh = Open("CON:0/10/640/190/FileSystem 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
X *  within 8 bytes of address (remember the first longword is a segment
X *  pointer and skipped).  Sigh....  (see CreateProc() above).
X */
X
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				/* HDEBUG */
END_OF_FILE
if test 26338 -ne `wc -c <'src/pack.c'`; then
    echo shar: \"'src/pack.c'\" unpacked with wrong size!
fi
# end of 'src/pack.c'
fi
echo shar: End of archive 4 \(of 6\).
cp /dev/null ark4isdone
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.