mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2025-01-14 21:29:53 +00:00
4325cdcc78
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included. The Tools directory, containing mostly junk, is also excluded.
2478 lines
78 KiB
OpenEdge ABL
2478 lines
78 KiB
OpenEdge ABL
{
|
|
File: dpPubIO.inc.p
|
|
|
|
Written by: Nick Kledzik
|
|
|
|
Copyright: © 1989-1990, 1992 by Apple Computer, Inc., all rights reserved.
|
|
|
|
This file is used in these builds: BigBang
|
|
|
|
Change History (most recent first):
|
|
|
|
<40> 1/20/92 ngk Fix bug in dpPubCloseFile. It was not switching to system mode
|
|
before closing the file. This is now required in CubeE.
|
|
<39> 4/5/91 ngk just added a comment about FindPublisher
|
|
<38> 4/3/91 ngk MM,#c1-MTM-007: Check for nil controlblock in OpenNewEdition.
|
|
<37> 3/22/91 ngk VL, #b6-ngk-005: check MemErr after SetHandleSize.
|
|
Add missing Success calls.
|
|
<36> 2/27/91 ngk MM,#83903: Move sending of read events to pubSync
|
|
from dp_EditionMgrBackGroundTask in dpEvents.inc.p
|
|
<35> 1/25/91 ngk VL, #?????: Fix DeleteEditionContainer to not reference a
|
|
control block after releasing it.
|
|
<34> 1/14/91 ngk <MM> Fix use count problem in dp_DeleteEditionContainerFile
|
|
<33> 12/14/90 ngk <KSM>Fix setting of type & creator til after copying of other
|
|
Finder info.
|
|
<32> 12/14/90 ngk <MM>Use new PBxxxSync and FSpXxx calls. Changed dpFindPublisher
|
|
to tell app to open edition if edition does not contain an alias.
|
|
Fix for "throw me away" bug, by retrying delete. Fixed some
|
|
failure handlers that were not cleaning up properly. Shrunk
|
|
window for throw me aways to be made by change the subscriber lock
|
|
strategy to try to read before retring lock - this distingushes
|
|
between the publisher lock and another subscriber lock. Change
|
|
Sync to only try FCBInfo if GetCatInfo failed - this prevents syncing
|
|
to temp file on FileShare during exchange files. Clear Inited bit
|
|
if overwriting non-edition file. Don't require edition to have been
|
|
open during SwapAndDelete.
|
|
<31> 12/5/90 dba <dnf> Get rid of conflicts of bit definitions in here with new
|
|
global definitions in Files.p.
|
|
<30> 10/30/90 ngk Don't track editions into trash. In PubClose, now free maps.
|
|
Remove sync param from PBDTGetPath.
|
|
<29> 9/21/90 ngk DC NIL ioNamePtr when calling PBDTGetPath
|
|
<28> 9/15/90 ngk Fixed memory leak in dpResolveAliasToPublisher if edition
|
|
does not contain an alias.
|
|
<27> 9/13/90 ngk Changed file type for edition files to be all lowercase.
|
|
<26> 8/28/90 dnf Changed references to ResolveFileID into references to
|
|
ResolveFileIDRef.
|
|
<25> 8/26/90 ngk Another fix to GetStandardFormats. It was decrementing the
|
|
usage count one too many if it encountered an error.
|
|
<24> 8/16/90 ngk In sync, if file is missing, then mark so in control block.
|
|
In GetStandardFormats, on error remember to close edition.
|
|
Now handle case of bad verb to standard opener by return paramErr.
|
|
Fix createEditionFile to not set script if it is smSystemScript(-1).
|
|
<23> 8/4/90 ngk fix createEditionFile to have type edtu.
|
|
<22> 7/31/90 ngk fix throw me away files. Fix register usage with failure handling.
|
|
<21> 7/14/90 ngk Fix sync of MFS floppies. Changed more bAND's to bTST's.
|
|
Put hack in to work around GetVolParms bug.
|
|
Fix dpStandardSubscriberClose when sectionH=NIL to dec use count.
|
|
Fix dpStandardSubscriberClose to not close file if it was open for writing.
|
|
Fix dpGetStandardFormats to release thePubCB on error.
|
|
<20> 7/2/90 ngk need to release pubcb in delete edition
|
|
<19> 7/2/90 ngk bAND to bTST. Rework dpResolveAliasToPublisher and
|
|
dpFindPublisher.
|
|
Changed HDelete to FSpDelete. Did a bunch of code saving munges.
|
|
DeleteEditionFile stops all I/O with subscribers and only deletes
|
|
the file if there are no register publishers.
|
|
<18> 6/20/90 ngk Changed CanonicalFileSpec to FSSpec.
|
|
<17> 6/1/90 ngk Add 'snd ' to GetStandardFormats and established a priority
|
|
for choosing the preview format.
|
|
<16> 5/31/90 ngk Added new edition file types and made dpStandardCanSubscribe a
|
|
routine.
|
|
<15> 5/3/90 ngk Added ResolveAliasToPublisher routine.
|
|
<14> 4/7/90 ngk Use dynamic arrays for allocation and format lists. Changed
|
|
dpPubSetInfo to dpPubSync. Use RangeLocking to avoid "Throw me
|
|
away" files. Use new FailOSErr stuff.
|
|
<13> 3/20/90 ngk Added script parameter to dp_CreateEditionContainerFile
|
|
<12> 3/16/90 dnf Change DTOpen to DTGetPath
|
|
<12> 3/16/90 dnf Change DTOpen to DTGetPath
|
|
<11> 3/10/90 ngk Use bHasFileIDs attribute of VolParms. Removed dpGetVolumeInfo
|
|
and call GetVolParms directly. Moved locking and unlocking of
|
|
package to dispatcher.
|
|
dpGetVolumeInfo and call GetVolParms directly.
|
|
Moved locking and unlocking of package to dispatcher.
|
|
<10> 2/26/90 ngk When previewing TEXT, only read in first 500 bytes.
|
|
<9> 2/25/90 ngk Fix replace-existing non-edition file bug.
|
|
<8> 2/16/90 ngk Publisher^^.mdDate is used to set the edition File's mdDate.
|
|
Fixed bug where edition's create date kepts changing.
|
|
<7> 2/4/90 ngk Fix bug header.formatLength of edition files. Allow
|
|
OpenNewEdition to have NIL sectionDocument. Get mdDate for
|
|
newEditions from section record. Now use PBDT calls to exchange
|
|
edition comments.
|
|
<6> 1/26/90 ngk Fixed calls to Alias manager to use new non-pointer style
|
|
<5> 1/22/90 ngk Fix standard formatIO routine to use symbolic name. Changed
|
|
publisher to OpenDenyOtherWriters.
|
|
<4> 1/8/90 ngk Renamed GetAppRefNum
|
|
<2+> 1/6/90 ngk Changed name of GetAppRefNum,removed eoCanPublish
|
|
<2> 1/6/90 ngk Convert to BBS, removed wakeUpTime. Added bottleneck routines.
|
|
renamed some routines.
|
|
<2.3> 11/13/89 ngk Added Locking and restoring of Pack11 in all public routines
|
|
<2.2> 11/04/89 ngk Removed call to PBHCreateID in NewPublicationFile
|
|
Changed AppRefNum to be a handle to app's globals.
|
|
<2.1> 10/25/89 ngk Moved some inline code out.
|
|
<2.0> 10/13/89 ngk nothing
|
|
<1.9> 10/02/89 ngk added wakeUpTime
|
|
<1.8> 09/18/89 ngk Added dp_DeleteEditionContainerFile
|
|
<1.7> 09/08/89 ngk Removed call to CreateID in NewPublication
|
|
<1.6> 09/07/89 ngk Changed DisposeAlias to DisposHandle
|
|
<1.5> 08/29/89 ngk Changed dpGetVolumeInfo to return VolumeServices. Changed pub open
|
|
strategy to always have pub files open for read/write if a
|
|
publisher is registered and Subscribers leave the file open for
|
|
read-only. dpBackGroundTask will close read-only pub files if
|
|
no Subscribers are doing I/O. Seperated the loading of allocMap
|
|
and formats into dpPubLoadMap and dpPubReleaseMap. Stop using fileIDs.
|
|
<1.4> 08/08/89 ngk Split dp_CloseEdition into closing after reading and after writing
|
|
Used new error codes. qDebug -> qCatchFailures & qRangeCheck
|
|
Started using new FileID calls.
|
|
<1.3> 06/11/89 ngk Changed file format to include a separate foramt list and allocation table
|
|
changed dpGetPubInfo to dpSetPubInfo
|
|
made interface to dpWriteFormat & dpReadFormat consistent
|
|
<1.2> 05/31/89 ngk Changed SofaLink usage to Aliases
|
|
<1.1> 05/29/89 ngk Removed VirtualOpen and VirtualClose. Changed AccessMode to my own
|
|
enumerated type. Added WriteFormat and ReadFormat to allow I/O
|
|
with a Section I/O Control Block, as needed for the "pointer" from
|
|
the Publication file back to the Publisher document.
|
|
<1.0> 05/19/89 ngk Submitted for first time
|
|
|
|
To Do:
|
|
}
|
|
|
|
|
|
{$IFC UNDEFINED qExtraDebug }
|
|
{$SETC qExtraDebug = FALSE}
|
|
{$ENDC}
|
|
|
|
{========================== low level ========================}
|
|
|
|
CONST
|
|
kThrowMeAwayName = 'throw me away';
|
|
kTicksBetweenDeleteRetries = 20;
|
|
kMaxDeleteRetries = 10;
|
|
kTicksUntilOpenRetry = 10;
|
|
|
|
TYPE
|
|
VolumeParms = RECORD
|
|
vMVersion: INTEGER;
|
|
vMAttrib: LONGINT;
|
|
vMLocalHand: LONGINT;
|
|
vMServerAdr: LONGINT;
|
|
{vMVolumeGrade: LONGINT;}
|
|
{vMAltPrivModel:INTEGER;}
|
|
END;
|
|
|
|
|
|
{========================== internal routines ========================}
|
|
|
|
|
|
{ this will be eventually moved into File system }
|
|
FUNCTION FSpOpenDeny(spec: FSSpec; denyModes: INTEGER; VAR refNum: INTEGER): OSErr;
|
|
VAR
|
|
PBH: HParamBlockRec;
|
|
BEGIN
|
|
WITH PBH DO
|
|
BEGIN
|
|
PBH.ioVRefNum := spec.vRefNum;
|
|
PBH.ioDirID := spec.parID;
|
|
PBH.ioNamePtr := @spec.name;
|
|
PBH.ioVersNum := 0;
|
|
PBH.ioMisc := NIL;
|
|
PBH.ioDenyModes := denyModes;
|
|
FSpOpenDeny := PBHOpenDenySync(@PBH);
|
|
refNum := ioRefNum;
|
|
END; {with}
|
|
END; { FSpOpenDeny }
|
|
|
|
|
|
{------------- dpNotifySubscribers -------------}
|
|
FUNCTION dpNotifySubscribers(thePubCB: PubCBHandle): OSErr;
|
|
|
|
PROCEDURE NotifyIfOutOfDateSubscriber(aSection: SectionHandle; inApp: AppRefNum);
|
|
BEGIN
|
|
WITH aSection^^ DO
|
|
BEGIN
|
|
IF controlBlock = Handle(thePubCB)
|
|
{&} THEN IF bTST(kind, kCanReadEditionsBit)
|
|
{&} THEN IF mode = sumAutomatic
|
|
{&} THEN IF mdDate <> thePubCB^^.info.mdDate THEN
|
|
BEGIN
|
|
IgnoreOSErr(dp_PostSectionEvent(aSection, inApp, sectionReadMsgID));
|
|
END; {if}
|
|
END; {with}
|
|
END; { NotifyIfOutOfDateSubscriber }
|
|
|
|
BEGIN
|
|
dpForEachSectionDo(NotifyIfOutOfDateSubscriber);
|
|
dpNotifySubscribers := noErr;
|
|
END; { dpNotifySubscribers }
|
|
|
|
|
|
|
|
{ makes sure a controlblock's fields are up to date with the file system world }
|
|
{------------- dpPubSync -------------}
|
|
FUNCTION dpPubSync(thePubCB: PubCBHandle): OSErr;
|
|
TYPE
|
|
VCBPtr = ^VCB;
|
|
VAR
|
|
PBH: HParamBlockRec;
|
|
PBF: FCBPBRec;
|
|
VP: VolumeParms;
|
|
theInfo: EditionInfoRecord;
|
|
infoErr: OSErr;
|
|
tempStr: Str63;
|
|
tempThrow: Str63;
|
|
p: VCBPtr;
|
|
trashVRefNum:INTEGER;
|
|
trashDirID: LONGINT;
|
|
BEGIN
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
BlockMove(@{thePubCB^^.}info, @theInfo, SizeOf(theInfo)); { theInfo := thePubCB^^.info; }
|
|
|
|
{ PBHGetVolParms only called once }
|
|
IF {thePubCB^^.}volumeInfo = 0 THEN
|
|
BEGIN
|
|
{ get volume attributes }
|
|
WITH PBH DO
|
|
BEGIN
|
|
ioVRefNum := theInfo.container.theFile.vRefNum;
|
|
ioNamePtr := NIL;
|
|
ioBuffer := @VP;
|
|
ioReqCount := SizeOf(VP);
|
|
END; {with}
|
|
|
|
{ If it fails, then volume has no attributes }
|
|
IF PBHGetVolParmsSync(@PBH) = noErr
|
|
THEN {thePubCB^^.}volumeInfo := VP.vMAttrib;
|
|
|
|
{### super hack to get around bug that MFS volumes return bHasFileIDs ###}
|
|
p := VCBPtr(GetVCBQHdr^.qHead);
|
|
WHILE (p<>NIL) & (p^.vcbVRefNum <> theInfo.container.theFile.vRefNum)
|
|
DO p := VCBPtr(p^.qLink);
|
|
IF (p <> NIL) & (p^.vcbSigWord = $D2D7 )
|
|
THEN bClr(thePubCB^^.volumeInfo, bHasFileIDs);
|
|
{### end of super hack ###}
|
|
END; {if}
|
|
|
|
{ get edition, create date, and creator }
|
|
PBH.ioNamePtr := @theInfo.container.theFile.name;
|
|
PBH.ioVRefNum := theInfo.container.theFile.vRefNum;
|
|
PBH.ioFDirIndex := 0;
|
|
PBH.ioFVersNum := 0;
|
|
PBH.ioDirID := theInfo.container.theFile.parID;
|
|
infoErr := PBGetCatInfoSync(@PBH);
|
|
|
|
IF infoErr = fnfErr THEN
|
|
BEGIN
|
|
{ if file is known to be missing no need to try any harder }
|
|
IF {thePubCB^^.}fileMissing THEN
|
|
BEGIN
|
|
dpPubSync := noErr;
|
|
EXIT(dpPubSync);
|
|
END; {if}
|
|
|
|
{ if file is open use FCB to track file }
|
|
IF {thePubCB^^.}fileRefNum <> kClosedFileRefNum THEN
|
|
BEGIN
|
|
{ file is open, can track down access path to get info }
|
|
PBF.ioNamePtr := @tempStr;
|
|
PBF.ioRefNum := {thePubCB^^.}fileRefNum;
|
|
PBF.ioFCBIndx := 0;
|
|
IF PBGetFCBInfoSync(@PBF) = noErr
|
|
{&} THEN IF PBF.ioFCBParID <> 0 THEN { only do this for HFS volumes }
|
|
BEGIN
|
|
{ don't sync to files that start with 'throw me away' }
|
|
tempThrow := kThrowMeAwayName;
|
|
IF NOT dpSameBytes(@tempStr[1], @tempThrow[1], LENGTH(tempThrow)) THEN
|
|
BEGIN
|
|
BlockMove(@tempStr, @theInfo.container.theFile.name, SizeOf(Str63));
|
|
theInfo.container.theFile.parID := PBF.ioFCBParID;
|
|
PBH.ioNamePtr := @theInfo.container.theFile.name; { <36, #83903> }
|
|
PBH.ioVRefNum := theInfo.container.theFile.vRefNum; { <36, #83903> }
|
|
PBH.ioFDirIndex := 0; { <36, #83903> }
|
|
PBH.ioDirID := theInfo.container.theFile.parID; { <36, #83903> }
|
|
infoErr := PBGetCatInfoSync(@PBH); { <36, #83903> }
|
|
END; {if}
|
|
END; {if}
|
|
END; {if}
|
|
END; {if}
|
|
|
|
IF infoErr <> noErr THEN
|
|
BEGIN
|
|
{ file was renamed or moved, try looking up by cNodeID }
|
|
IF bTST({thePubCB^^.}volumeInfo, bHasFileIDs) THEN
|
|
BEGIN
|
|
PBH.ioVRefNum := theInfo.container.theFile.vRefNum;
|
|
PBH.ioFileID := {thePubCB^^.}pubCNodeID;
|
|
PBH.ioNamePtr := @theInfo.container.theFile.name;
|
|
IF PBResolveFileIDRefSync(@PBH) = noErr THEN
|
|
BEGIN
|
|
{ make sure that it was not moved into the trash }
|
|
IF FindFolder(theInfo.container.theFile.vRefNum,{vRefNum}
|
|
kTrashFolderType, {folderType}
|
|
FALSE, {createFolder}
|
|
trashVRefNum, {foundVRefNum}
|
|
trashDirID) = noErr THEN {foundDirID}
|
|
BEGIN
|
|
IF PBH.ioDirID <> trashDirID THEN
|
|
BEGIN
|
|
{ if found, but not in trash, then sync to new location }
|
|
theInfo.container.theFile.parID := PBH.ioSrcDirID;
|
|
PBH.ioNamePtr := @theInfo.container.theFile.name;
|
|
PBH.ioVRefNum := theInfo.container.theFile.vRefNum;
|
|
PBH.ioFDirIndex := 0;
|
|
PBH.ioDirID := theInfo.container.theFile.parID;
|
|
infoErr := PBGetCatInfoSync(@PBH);
|
|
END
|
|
ELSE infoErr := fnfErr;
|
|
END; {if}
|
|
END; {if}
|
|
END; {if}
|
|
END; {if}
|
|
|
|
{ files can not be found so mark it so }
|
|
IF infoErr = fnfErr
|
|
THEN {thePubCB^^.}fileMissing := TRUE;
|
|
|
|
IF infoErr = noErr THEN
|
|
BEGIN
|
|
{ update control block }
|
|
BlockMove(@theInfo.container.theFile, @info.container.theFile, SizeOf(FSSpec)); { info.container.theFile := theInfo.container.theFile; }
|
|
info.crDate := PBH.ioFlCrDat;
|
|
info.fdCreator := PBH.ioFlFndrInfo.fdCreator;
|
|
info.fdType := PBH.ioFlFndrInfo.fdType;
|
|
pubCNodeID := PBH.ioDirID;
|
|
|
|
{ file definitely exists now. This reverses 'fileMissing' if file should reappear }
|
|
{thePubCB^^.}fileMissing := FALSE;
|
|
|
|
{ Whenever the control block's modDate needs to be updated, read events need to be sent }
|
|
IF info.mdDate <> PBH.ioFlMdDat THEN { <36, #83903> }
|
|
BEGIN { <36, #83903> }
|
|
info.mdDate := PBH.ioFlMdDat; { <36, #83903> }
|
|
IgnoreOSErr(dpNotifySubscribers(thePubCB)); { <36, #83903> }
|
|
END; { <36, #83903> }
|
|
END; {if}
|
|
|
|
END; {with}
|
|
dpPubSync := infoErr;
|
|
END; { dpPubSync }
|
|
|
|
|
|
|
|
{------------- dpPubOpenFile -------------}
|
|
FUNCTION dpPubOpenFile(thePubCB: PubCBHandle; kind: SectionType): OSErr;
|
|
VAR
|
|
theRefNum: INTEGER;
|
|
pubFile: FSSpec;
|
|
transPerm: INTEGER;
|
|
hfsPerm: INTEGER;
|
|
orgOpenMode: INTEGER;
|
|
PBH: HParamBlockRec;
|
|
inSystemMode: BOOLEAN;
|
|
anErr: OSErr;
|
|
ignoreLong: LONGINT;
|
|
fi: FailInfo;
|
|
BEGIN
|
|
DoNotPutInRegister(@inSystemMode);
|
|
DoNotPutInRegister(@orgOpenMode);
|
|
inSystemMode := FALSE;
|
|
theRefNum := kClosedFileRefNum;
|
|
orgOpenMode := thePubCB^^.openMode;
|
|
pubFile := thePubCB^^.info.container.theFile;
|
|
|
|
{ set up failure handling }
|
|
IF isFailure(fi, dpPubOpenFile) THEN
|
|
BEGIN
|
|
IF inSystemMode THEN IgnoreOSErr(EndSystemMode);
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
fileRefNum := kClosedFileRefNum;
|
|
openMode := dmNotOpen;
|
|
END; {with}
|
|
IF orgOpenMode <> dmNotOpen THEN
|
|
BEGIN
|
|
{$IFC qExtraDebug }
|
|
DebugStr('dpPubOpenFile: Failing and re-opening file');
|
|
{$ENDC }
|
|
{ failed opening for more permssions, so reopen with less }
|
|
CASE orgOpenMode OF
|
|
dmRequestReadPerm:
|
|
IgnoreOSErr(dpPubOpenFile(thePubCB, stSubscriber));
|
|
dmRequestReadPerm+dmRequestWritePerm+dmDenyOtherWriters:
|
|
IgnoreOSErr(dpPubOpenFile(thePubCB, stSubscriber));
|
|
END; {case}
|
|
END; {if}
|
|
EXIT(dpPubOpenFile);
|
|
END; {if}
|
|
|
|
{ do translation to deny mode permissions }
|
|
transPerm := 0;
|
|
IF bTST(kind, kCanReadEditionsBit)
|
|
THEN transPerm := bOR(transPerm, dmRequestReadPerm);
|
|
|
|
IF bTST(kind, kCanWriteEditionsBit)
|
|
THEN transPerm := bOR(transPerm, dmRequestWritePerm+dmRequestReadPerm+dmDenyOtherWriters);
|
|
|
|
{ see if already open with enough permissions }
|
|
{ i.e. a subscriber tries to open, but publisher already has it open }
|
|
IF bAND(orgOpenMode, transPerm) = transPerm THEN
|
|
BEGIN
|
|
{ already open with enough perms }
|
|
dpPubOpenFile := noErr;
|
|
Success(fi);
|
|
EXIT(dpPubOpenFile);
|
|
END;
|
|
|
|
{ to handle case of subscriber already has it open and then a publisher }
|
|
{ tries to open it, close the file and reopen with more permissions }
|
|
IF orgOpenMode <> dmNotOpen THEN
|
|
BEGIN
|
|
FailOSErr(dpPubCloseFile(thePubCB, NOT kFlush));
|
|
END;
|
|
|
|
IF bTST(thePubCB^^.volumeInfo, bHasOpenDeny) THEN
|
|
BEGIN
|
|
FailOSErr(BeginSystemMode); inSystemMode := TRUE;
|
|
{ open file of given spec }
|
|
anErr := FSpOpenDeny(pubFile, transPerm, theRefNum);
|
|
IF anErr = fnfErr {&} THEN IF (NOT bTST(thePubCB^^.volumeInfo, bHasFileIDs)) THEN
|
|
BEGIN
|
|
{ there is a small time window, during swap and delete when the file is gone. }
|
|
{ for this case, wait a moment and retry. }
|
|
Delay({numTicks}kTicksUntilOpenRetry, {VAR finalTicks}ignoreLong);
|
|
anErr := FSpOpenDeny(pubFile, transPerm, theRefNum);
|
|
END;
|
|
{$IFC qExtraDebug }
|
|
IF anErr <> noErr
|
|
THEN DebugStr('dpPubOpenFile: OpenDeny failed');
|
|
{$ENDC }
|
|
FailOSErr(anErr);
|
|
FailOSErr(EndSystemMode); inSystemMode := FALSE;
|
|
END ELSE
|
|
BEGIN
|
|
{ do translation to HFS permissions }
|
|
hfsPerm := 0;
|
|
IF bTST(kind, kCanWriteEditionsBit)
|
|
THEN hfsPerm := fsRdWrPerm
|
|
ELSE IF bTST(kind, kCanReadEditionsBit)
|
|
THEN hfsPerm := fsRdPerm
|
|
ELSE FailOSErr(permErr);
|
|
|
|
FailOSErr(BeginSystemMode); inSystemMode := TRUE;
|
|
{ open file using spec in thePubCB }
|
|
WITH PBH DO
|
|
BEGIN
|
|
ioNamePtr := @pubFile.name;
|
|
ioVRefNum := pubFile.vRefNum;
|
|
ioDirID := pubFile.parID;
|
|
ioVersNum := 0;
|
|
ioMisc := NIL;
|
|
ioPermssn := hfsPerm;
|
|
FailOSErr(PBHOpenSync(@PBH));
|
|
theRefNum := ioRefNum;
|
|
END; {with}
|
|
FailOSErr(EndSystemMode); inSystemMode := FALSE;
|
|
END; {if}
|
|
|
|
{ set more info fields }
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
fileRefNum := theRefNum;
|
|
openMode := transPerm;
|
|
END; {with}
|
|
|
|
Success(fi);
|
|
|
|
END; { dpPubOpenFile }
|
|
|
|
|
|
|
|
|
|
{------------- dpPubFlushFile -------------}
|
|
{ only used after writing a new edition }
|
|
FUNCTION dpPubFlushFile(thePubCB: PubCBHandle): OSErr;
|
|
VAR
|
|
theMap: AllocationMapHandle;
|
|
theFormats: FormatListHandle;
|
|
theRefNum: INTEGER;
|
|
header: EditionFileMinHeader;
|
|
PB: ParamBlockRec;
|
|
i: INTEGER;
|
|
|
|
BEGIN
|
|
{ get pub file refnum & map handle }
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
theRefNum := fileRefNum;
|
|
theMap := allocMap;
|
|
theFormats := formats;
|
|
END; {with}
|
|
|
|
{ make new header }
|
|
WITH header DO
|
|
BEGIN
|
|
version := kMapVersion;
|
|
formatsOffset := bAND(thePubCB^^.fileMark+$F,$FFFFFFF0); { round up to nearest paragraph }
|
|
formatsLength := theFormats^^.lastUsedSlot * SizeOf(Format) + SizeOf(INTEGER);
|
|
mapOffset := bAND(formatsOffset+formatsLength+$F,$FFFFFFF0); { round up to nearest paragraph }
|
|
mapLength := theMap^^.lastUsedSlot * SizeOf(AllocationRecord) + SizeOf(INTEGER);
|
|
END; {with}
|
|
|
|
{ write formats out }
|
|
WITH PB DO
|
|
BEGIN
|
|
ioRefNum := theRefNum;
|
|
ioBuffer := @theFormats^^.lastUsedSlot;
|
|
ioReqCount := header.formatsLength;
|
|
ioPosMode := fsFromStart;
|
|
ioPosOffset := header.formatsOffset;
|
|
FailOSErr(PBWriteSync(@PB));
|
|
IF ioActCount <> header.formatsLength THEN FailOSErr(dskFulErr); {when would this ever happen?}
|
|
END; {with}
|
|
|
|
{ write map out }
|
|
WITH PB DO
|
|
BEGIN
|
|
ioRefNum := theRefNum;
|
|
ioBuffer := @theMap^^.lastUsedSlot;
|
|
ioReqCount := header.mapLength;
|
|
ioPosMode := fsFromStart;
|
|
ioPosOffset := header.mapOffset;
|
|
FailOSErr(PBWriteSync(@PB));
|
|
IF ioActCount <> header.mapLength THEN FailOSErr(dskFulErr); {when would this ever happen?}
|
|
END; {with}
|
|
|
|
|
|
{ write header out }
|
|
WITH PB DO
|
|
BEGIN
|
|
ioRefNum := theRefNum;
|
|
ioBuffer := @header;
|
|
ioReqCount := SizeOf(header);
|
|
ioPosMode := fsFromStart;
|
|
ioPosOffset := 0;
|
|
FailOSErr(PBWriteSync(@PB));
|
|
IF ioActCount <> SizeOf(header) THEN FailOSErr(dskFulErr); {when would this ever happen?}
|
|
END; {with}
|
|
|
|
{ call _flushFile }
|
|
WITH PB DO
|
|
BEGIN
|
|
ioRefNum := theRefNum;
|
|
FailOSErr(PBFlushFileSync(@PB));
|
|
END; {with}
|
|
|
|
{### should we call flush volume? }
|
|
dpPubFlushFile := noErr;
|
|
END; { dpPubFlushFile }
|
|
|
|
|
|
|
|
{------------- dpPubCloseFile -------------}
|
|
FUNCTION dpPubCloseFile(thePubCB: PubCBHandle; flush: BOOLEAN): OSErr;
|
|
VAR
|
|
PB: ParamBlockRec;
|
|
theRefNum: INTEGER;
|
|
BEGIN
|
|
theRefNum := thePubCB^^.fileRefNum;
|
|
|
|
{$IFC qRangeCheck}
|
|
IF flush {&} THEN IF NOT bTST(thePubCB^^.openMode, dmRequestWritePermBit)
|
|
THEN DebugStr('dpPubCloseFile: trying to flush a read-only pub.');
|
|
IF theRefNum = kClosedFileRefNum
|
|
THEN DebugStr('dpPubCloseFile: trying to close a closed pub file.');
|
|
{$ENDC}
|
|
|
|
{ write out map to disk if needed }
|
|
IF flush THEN FailOSErr(dpPubFlushFile(thePubCB));
|
|
|
|
{ free the map, cause it is invalid once the file is closed }
|
|
IF thePubCB^^.allocMap <> NIL
|
|
THEN IgnoreOSErr(dpPubReleaseMap(thePubCB));
|
|
|
|
{ mark file closed in the PCB }
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
fileRefNum := kClosedFileRefNum;
|
|
openMode := dmNotOpen;
|
|
rangeLockStart := 0; { once the file is closed, range locks are invalid }
|
|
rangeLockLen := 0;
|
|
fileMark := kBadPosition;
|
|
END; {with}
|
|
|
|
|
|
{ close file }
|
|
WITH PB DO
|
|
BEGIN
|
|
{ need to be in system mode to close this file }
|
|
IgnoreOSErr(BeginSystemMode);
|
|
|
|
ioRefNum := theRefNum;
|
|
dpPubCloseFile := PBCloseSync(@PB);
|
|
|
|
IgnoreOSErr(EndSystemMode);
|
|
END; {with}
|
|
|
|
END; { dpPubCloseFile }
|
|
|
|
|
|
|
|
|
|
{------------- dpPubLoadMap -------------}
|
|
FUNCTION dpPubLoadMap(thePubCB: PubCBHandle; kind: SectionType): OSErr;
|
|
VAR
|
|
theRefNum: INTEGER;
|
|
header: EditionFileMinHeader;
|
|
PBH: HParamBlockRec;
|
|
theMap: AllocationMapHandle;
|
|
theFormats: FormatListHandle;
|
|
justOpened: BOOLEAN;
|
|
slots: INTEGER;
|
|
remainder: INTEGER;
|
|
fi: FailInfo;
|
|
BEGIN
|
|
DoNotPutInRegister(@theMap);
|
|
DoNotPutInRegister(@theFormats);
|
|
DoNotPutInRegister(@justOpened);
|
|
theMap := NIL;
|
|
theFormats := NIL;
|
|
justOpened := FALSE;
|
|
|
|
{ set up failure handling }
|
|
IF isFailure(fi, dpPubLoadMap) THEN
|
|
BEGIN
|
|
IF justOpened THEN IgnoreOSErr(dpPubCloseFile(thePubCB, NOT kFlush));
|
|
IF theMap <> NIL THEN DisposHandle(Handle(theMap));
|
|
IF theFormats <> NIL THEN DisposHandle(Handle(theFormats));
|
|
EXIT(dpPubLoadMap);
|
|
END; {if}
|
|
|
|
{ make sure the file is open }
|
|
IF thePubCB^^.openMode = dmNotOpen THEN
|
|
BEGIN
|
|
FailOSErr(dpPubOpenFile(thePubCB, kind));
|
|
justOpened := TRUE;
|
|
END;
|
|
|
|
(*
|
|
{$IFC qRangeCheck}
|
|
IF thePubCB^^.openMode <> kind
|
|
THEN DebugStr('dpPubLoadMap: already open for wrong access');
|
|
{$ENDC}
|
|
*)
|
|
|
|
CASE kind OF
|
|
stPublisher:
|
|
BEGIN
|
|
{ create a map for allocation }
|
|
FailOSErr(dpNewDynamicArray(0, {headerSize}
|
|
SizeOf(AllocationRecord), {slotSize}
|
|
kInitialAllocCount, {initialSlots}
|
|
NewHandleSys, {MyNewHandle}
|
|
theMap)); {VAR arrayH}
|
|
|
|
{ create a format list for an edition with no formats }
|
|
FailOSErr(dpNewDynamicArray(0, {headerSize}
|
|
SizeOf(Format), {slotSize}
|
|
kInitialFormats,{initialSlots}
|
|
NewHandleSys, {MyNewHandle}
|
|
theFormats)); {VAR arrayH}
|
|
END;
|
|
|
|
stSubscriber:
|
|
BEGIN
|
|
{ if starting to read then get map and format info from file }
|
|
|
|
theRefNum := thePubCB^^.fileRefNum;
|
|
{ read header from file }
|
|
WITH PBH DO
|
|
BEGIN
|
|
ioRefNum := theRefNum;
|
|
ioBuffer := @header;
|
|
ioReqCount := 20; { only required part of header }
|
|
ioPosMode := fsFromStart;
|
|
ioPosOffset := 0;
|
|
FailOSErr(PBReadSync(@PBH));
|
|
IF ioActCount <> 20 THEN FailOSErr(badEditionFileErr);
|
|
END; {with}
|
|
|
|
{ check header for validity }
|
|
IF header.version <> kMapVersion THEN FailOSErr(badEditionFileErr);
|
|
|
|
{ allocate space for formats, read it from file }
|
|
UnsignedDivide(header.formatsLength-SizeOf(INTEGER), {numer}
|
|
SizeOf(Format), {denom}
|
|
slots, {quotient}
|
|
remainder); {remainder}
|
|
{$IFC qRangeCheck }
|
|
IF remainder <> 0 THEN DebugStr('dpPubLoadMap: bad formats size');
|
|
{$ENDC }
|
|
FailOSErr(dpNewDynamicArray(0, {headerSize}
|
|
SizeOf(Format), {slotSize}
|
|
slots, {initialSlots}
|
|
NewHandleSys, {MyNewHandle}
|
|
theFormats)); {VAR arrayH}
|
|
theFormats^^.lastUsedSlot := slots;
|
|
HLock(Handle(theFormats));
|
|
WITH PBH DO
|
|
BEGIN
|
|
ioRefNum := theRefNum;
|
|
ioBuffer := @theFormats^^.lastUsedSlot;
|
|
ioReqCount := header.formatsLength;
|
|
ioPosMode := fsFromStart;
|
|
ioPosOffset := header.formatsOffset;
|
|
FailOSErr(PBReadSync(@PBH));
|
|
IF ioActCount <> header.formatsLength THEN FailOSErr(badEditionFileErr);
|
|
END; {with}
|
|
HUnLock(Handle(theFormats));
|
|
|
|
{ allocate space for map, read it from file }
|
|
UnsignedDivide(header.mapLength-SizeOf(INTEGER), {numer}
|
|
SizeOf(AllocationRecord), {denom}
|
|
slots, {quotient}
|
|
remainder); {remainder}
|
|
{$IFC qRangeCheck }
|
|
IF remainder <> 0 THEN DebugStr('dpPubLoadMap: bad formats size');
|
|
{$ENDC }
|
|
FailOSErr(dpNewDynamicArray(0, {headerSize}
|
|
SizeOf(AllocationRecord), {slotSize}
|
|
slots, {initialSlots}
|
|
NewHandleSys, {MyNewHandle}
|
|
theMap)); {VAR arrayH}
|
|
theMap^^.lastUsedSlot := slots;
|
|
HLock(Handle(theMap));
|
|
WITH PBH DO
|
|
BEGIN
|
|
ioRefNum := theRefNum;
|
|
ioBuffer := @theMap^^.lastUsedSlot;
|
|
ioReqCount := header.mapLength;
|
|
ioPosMode := fsFromStart;
|
|
ioPosOffset := header.mapOffset;
|
|
FailOSErr(PBReadSync(@PBH));
|
|
IF ioActCount <> header.mapLength THEN FailOSErr(badEditionFileErr);
|
|
END; {with}
|
|
HUnLock(Handle(theMap));
|
|
END;
|
|
END; {case}
|
|
|
|
{ set more info fields }
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
allocMap := theMap;
|
|
formats := theFormats;
|
|
fileMark := SizeOf(EditionFileHeader); { really only needed for writing }
|
|
END; {with}
|
|
|
|
Success(fi);
|
|
END; { dpPubLoadMap }
|
|
|
|
|
|
|
|
{------------- dpPubReleaseMap -------------}
|
|
FUNCTION dpPubReleaseMap(thePubCB: PubCBHandle): OSErr;
|
|
BEGIN
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
{ dispose of the allocation map }
|
|
DisposHandle(Handle(allocMap));
|
|
allocMap := NIL;
|
|
|
|
{ dispose of the format list }
|
|
DisposHandle(Handle(formats));
|
|
formats := NIL;
|
|
END; {with}
|
|
|
|
dpPubReleaseMap := noErr;
|
|
END; { dpPubReleaseMap }
|
|
|
|
|
|
|
|
|
|
{------------- dpTweakCatInfo -------------}
|
|
FUNCTION dpTweakCatInfo(edition: FSSpec;
|
|
PROCEDURE Tweaker(VAR PBC: CInfoPBRec) ): OSErr;
|
|
VAR
|
|
PBC: CInfoPBRec;
|
|
BEGIN
|
|
WITH PBC, edition DO
|
|
BEGIN
|
|
{ set up param block }
|
|
ioNamePtr := @name;
|
|
ioVRefNum := vRefNum;
|
|
ioFDirIndex := 0;
|
|
ioFVersNum := 0;
|
|
ioDirID := parID;
|
|
FailOSErr(PBGetCatInfoSync(@PBC));
|
|
|
|
{ tweak the param block }
|
|
Tweaker(PBC);
|
|
|
|
{ write out the tweaked param block }
|
|
ioNamePtr := @name;
|
|
ioVRefNum := vRefNum;
|
|
ioFDirIndex := 0;
|
|
ioDirID := parID;
|
|
FailOSErr(PBSetCatInfoSync(@PBC));
|
|
END; {with}
|
|
|
|
dpTweakCatInfo := noErr;
|
|
END; { dpTweakCatInfo }
|
|
|
|
|
|
|
|
{------------- dpRandomFileName -------------}
|
|
PROCEDURE dpRandomFileName(VAR seedName: Str63);
|
|
VAR
|
|
timeNumber: Str255;
|
|
seedLen: INTEGER;
|
|
timeLen: INTEGER;
|
|
BEGIN
|
|
NumToString(TickCount, timeNumber);
|
|
seedLen := LENGTH(seedName);
|
|
timeLen := LENGTH(timeNumber);
|
|
IF seedLen + timeLen <= 31 THEN
|
|
BEGIN
|
|
BlockMove( Ptr(ORD(@timeNumber)+1),
|
|
Ptr(ORD(@seedName)+seedLen+1),
|
|
timeLen );
|
|
seedName[0] := CHAR(SeedLen + timeLen);
|
|
END ELSE
|
|
BEGIN
|
|
BlockMove( Ptr(ORD(@timeNumber)+1),
|
|
Ptr(ORD(@seedName)+31-timeLen),
|
|
TimeLen );
|
|
seedName[0] := CHAR(31);
|
|
END; {if}
|
|
END; { dpRandomFileName }
|
|
|
|
|
|
{------------- dpSwapAndDelete -------------}
|
|
FUNCTION dpSwapAndDelete(orgPubCB, newPubCB: PubCBHandle): OSErr;
|
|
VAR
|
|
PBH: HParamBlockRec;
|
|
PBC: CInfoPBRec;
|
|
orgEdition: FSSpec;
|
|
newEdition: FSSpec;
|
|
throwAway: FSSpec;
|
|
info1: FInfo;
|
|
info2: FXInfo;
|
|
originalCrDate: TimeStamp;
|
|
finderComments: Str255;
|
|
deleteErr: OSErr;
|
|
exchangeErr: OSErr;
|
|
setCommentErr: OSErr;
|
|
getCommentErr: OSErr;
|
|
openDTErr: OSErr;
|
|
DTRefNum: INTEGER;
|
|
i: INTEGER;
|
|
ignoreLong: LONGINT;
|
|
fileToDelete: FSSpecPtr;
|
|
wasNotEdition: BOOLEAN;
|
|
fi: FailInfo;
|
|
|
|
PROCEDURE dpSetFinderInfo(VAR PBC: CInfoPBRec);
|
|
CONST
|
|
hasBeenInited = 8;
|
|
VAR
|
|
tempLong: LONGINT;
|
|
BEGIN
|
|
WITH newPubCB^^.info DO
|
|
BEGIN
|
|
{ only need to set other info if we can't use exchangeFiles }
|
|
IF NOT bTST(orgPubCB^^.volumeInfo, bHasFileIDs) THEN
|
|
BEGIN
|
|
PBC.ioFlFndrInfo := info1;
|
|
PBC.ioFlXFndrInfo := info2;
|
|
PBC.ioFlCrDat := originalCrDate;
|
|
END;
|
|
{ set new type and creator }
|
|
PBC.ioFlFndrInfo.fdCreator := fdCreator;
|
|
PBC.ioFlFndrInfo.fdType := fdType;
|
|
{ if we are overwriting a non-edition file, we need to let the }
|
|
{ Finder know to re-instantiate the object for this file. }
|
|
IF wasNotEdition THEN
|
|
BEGIN
|
|
tempLong := PBC.ioFlFndrInfo.fdFlags;
|
|
bCLR(tempLong, hasBeenInited);
|
|
PBC.ioFlFndrInfo.fdFlags := tempLong;
|
|
END;
|
|
END; {with}
|
|
END; { dpSetFinderInfo }
|
|
|
|
BEGIN
|
|
{ set up failure handling }
|
|
IF isFailure(fi, dpSwapAndDelete) THEN
|
|
BEGIN
|
|
{ well, all hell broke loose. Lets at least close the }
|
|
{ new file as if it did swap. }
|
|
IgnoreOSErr(dpPubCloseFile(newPubCB, NOT kFlush));
|
|
EXIT(dpSwapAndDelete);
|
|
END; {if}
|
|
|
|
{ switch contents of old and new pub file }
|
|
{$IFC qRangeCheck }
|
|
IF original.vRefNum <> newEdition.vRefNum
|
|
THEN DebugStr('dpSwapAndDelete: files on different volumes!');
|
|
{$ENDC}
|
|
|
|
{ make sure the original was not moved during write of new edition }
|
|
IgnoreOSErr(dpPubSync(orgPubCB));
|
|
|
|
{ make local copies of files specs do reduce code size }
|
|
BlockMove(@orgPubCB^^.info.container.theFile, @orgEdition, SizeOf(FSSpec));
|
|
BlockMove(@newPubCB^^.info.container.theFile, @newEdition, SizeOf(FSSpec));
|
|
|
|
{ remember if we are overwriting a non-edition file }
|
|
wasNotEdition := NOT IsEditionFile(orgPubCB^^.info.fdType);
|
|
|
|
{$IFC qRangeCheck }
|
|
{ if original pubCB file is open, close it }
|
|
IF orgPubCB^^.openMode = dmNotOpen THEN
|
|
BEGIN
|
|
DebugStr('dpSwapAndDelete: org edition file not open.');
|
|
IF orgPubCB^^.usageInfo^^.totalIOCount <> 0
|
|
THEN DebugStr('dpSwapAndDelete: could not kill off all subscribers.');
|
|
END; {if}
|
|
{$ENDC }
|
|
|
|
IF bTST(orgPubCB^^.volumeInfo, bHasFileIDs) THEN
|
|
BEGIN
|
|
{ Use ExchangeFiles }
|
|
FailOSErr(FSpExchangeFiles(orgEdition, newEdition));
|
|
|
|
{ set type and creator of to match contents }
|
|
FailOSErr(dpTweakCatInfo(orgEdition, dpSetFinderInfo));
|
|
|
|
{ remember which file to delete }
|
|
fileToDelete := @newEdition;
|
|
END ELSE
|
|
BEGIN
|
|
{ exchangeFiles is not implemented, so do it by hand }
|
|
|
|
{ get Finder info from orginal file }
|
|
WITH PBC DO
|
|
BEGIN
|
|
ioNamePtr := @orgEdition.name;
|
|
ioVRefNum := orgEdition.vRefNum;
|
|
ioFDirIndex := 0;
|
|
ioFVersNum := 0;
|
|
ioDirID := orgEdition.parID;
|
|
FailOSErr(PBGetCatInfoSync(@PBC));
|
|
info1 := ioFlFndrInfo;
|
|
info2 := ioFlXFndrInfo;
|
|
originalCrDate := ioFlCrDat;
|
|
END; {with}
|
|
|
|
{ move Finder info to new edition file, except for type and creator }
|
|
FailOSErr(dpTweakCatInfo(newEdition, dpSetFinderInfo));
|
|
|
|
{ get original's Finder comments if able }
|
|
finderComments := '';
|
|
IF bTST(orgPubCB^^.volumeInfo, bHasDesktopMgr) THEN
|
|
BEGIN
|
|
WITH PBH DO
|
|
BEGIN
|
|
ioVRefNum := orgEdition.vRefnum;
|
|
ioNamePtr := NIL; { <29> }
|
|
openDTErr := PBDTGetPath(@PBH);
|
|
{$IFC qCatchFailures}
|
|
IF (openDTErr <> noErr)
|
|
THEN DebugStr('dpSwapAndDelete: PBDTOpen failed.');
|
|
{$ENDC}
|
|
DTRefNum := ioRefNum;
|
|
END;
|
|
|
|
{ continue if could open desk top database }
|
|
IF openDTErr = noErr THEN
|
|
BEGIN
|
|
WITH PBH DO
|
|
BEGIN
|
|
ioNamePtr := @orgEdition.name;
|
|
ioRefNum := DTRefNum;
|
|
ioDirID := orgEdition.parID;
|
|
ioBuffer := @finderComments[1];
|
|
ioReqCount := 255;
|
|
END; {with}
|
|
getCommentErr := PBDTGetCommentSync(@PBH);
|
|
IF getCommentErr = noErr
|
|
THEN finderComments[0] := CHR(PBH.ioActCount);
|
|
{$IFC qCatchFailures}
|
|
IF (getCommentErr <> noErr)
|
|
{&} THEN IF (getCommentErr <> afpItemNotFound)
|
|
{&} THEN IF (getCommentErr <> fnfErr){## bug in DTmanger}
|
|
THEN DebugStr('dpSwapAndDelete: GetComment failed.');
|
|
{$ENDC}
|
|
END; {if}
|
|
END; {if}
|
|
|
|
{ if original had finder comments, set the new one to have the same }
|
|
IF LENGTH(finderComments) <> 0 THEN
|
|
BEGIN
|
|
WITH PBH DO
|
|
BEGIN
|
|
ioNamePtr := @newEdition.name;
|
|
ioRefNum := DTRefNum;
|
|
ioDirID := newEdition.parID;
|
|
ioBuffer := @finderComments[1];
|
|
ioReqCount := LENGTH(finderComments);
|
|
setCommentErr:= PBDTSetCommentSync(@PBH);
|
|
END;
|
|
{$IFC qCatchFailures}
|
|
IF setCommentErr <> noErr THEN DebugStr('dpSwapAndDelete: PBDTSetComment failed.');
|
|
{$ENDC}
|
|
END; {if}
|
|
|
|
{ rename orginal Edition to temp name }
|
|
throwAway.vRefNum := orgEdition.vRefNum;
|
|
throwAway.parID := orgEdition.parID;
|
|
throwAway.name := kThrowMeAwayName;
|
|
dpRandomFileName(throwAway.name);
|
|
FailOSErr(FSpRename(orgEdition, throwAway.name));
|
|
|
|
{ rename new Edition to original name }
|
|
FailOSErr(FSpRename(newEdition, orgEdition.name));
|
|
|
|
{ remember which file to delete }
|
|
fileToDelete := @throwAway;
|
|
END; {if}
|
|
|
|
{ close original (if it was open) now that we are done }
|
|
{ it could be close now becuase 1) we are overwriting a non-edition file }
|
|
{ or 2) there was multiple publishers on this machine and the publisher }
|
|
{ unregistered while this publishers was between OpenNew/CloseEdition. }
|
|
IF orgPubCB^^.fileRefNum <> kClosedFileRefNum
|
|
THEN IgnoreOSErr(dpPubCloseFile(orgPubCB, NOT kFlush));
|
|
|
|
{ delete original file }
|
|
deleteErr := FSpDelete(fileToDelete^);
|
|
{$IFC qExtraDebug }
|
|
IF deleteErr <> noErr
|
|
THEN DebugStr('dpSwapAndDelete: final delete failed');
|
|
{$ENDC }
|
|
|
|
{ There is a small window between the close and delete, during which a subscriber could }
|
|
{ add a range lock. When this happens, a subscriber has the "throw me away" file open, }
|
|
{ so the delete fails. The fix for this is to wait for the subscriber to close, then delete. }
|
|
{ We don't have to worry about another subscriber re-opening, because they don't }
|
|
{ open files called "throw me away". }
|
|
FOR i := 1 TO kMaxDeleteRetries DO
|
|
BEGIN
|
|
IF deleteErr = noErr THEN LEAVE; {for}
|
|
Delay({numTicks}kTicksBetweenDeleteRetries, {VAR finalTicks}ignoreLong);
|
|
deleteErr := FSpDelete(fileToDelete^);
|
|
END; {for}
|
|
FailOSErr(deleteErr);
|
|
|
|
Success(fi); { <37> }
|
|
END; { dpSwapAndDelete }
|
|
|
|
|
|
|
|
{------------- dpTryToGetFormat -------------}
|
|
FUNCTION dpTryToGetFormat(whichFormat: FormatType; dataHandle: Handle;
|
|
ioRefNum: LONGINT; ioProc: FormatIOProcPtr): OSErr;
|
|
CONST
|
|
kMaxTextPreviewSize = 500; { max byte count of a text preview }
|
|
VAR
|
|
IOParams: FormatIOParamBlock;
|
|
fi: FailInfo;
|
|
hasFormatErr: OSErr;
|
|
BEGIN
|
|
{ failure handler needed }
|
|
IF IsFailure(fi, dpTryToGetFormat)
|
|
THEN EXIT(dpTryToGetFormat);
|
|
|
|
{ see if format is available }
|
|
IOParams.ioRefNum := ioRefNum;
|
|
IOParams.format := whichFormat;
|
|
FailOSErr(dp_CallFormatIOProc(ioHasFormat, IOParams, ioProc));
|
|
|
|
{ limit text size }
|
|
IF LONGINT(whichFormat) = LONGINT('TEXT') THEN
|
|
BEGIN
|
|
IF IOParams.buffLen > kMaxTextPreviewSize
|
|
THEN IOParams.buffLen := kMaxTextPreviewSize;
|
|
END; {if}
|
|
|
|
{ resize handle to hold it }
|
|
SetHandleSize(dataHandle, IOParams.buffLen);
|
|
FailOSErr(MemError); { <37> }
|
|
HLock(dataHandle);
|
|
|
|
{ read in format into handle }
|
|
IOParams.ioRefNum := ioRefNum;
|
|
IOParams.format := whichFormat;
|
|
{IOParams.formatIndex already set}
|
|
IOParams.offset := 0;
|
|
IOParams.buffPtr := dataHandle^;
|
|
{IOParams.buffLen already set }
|
|
FailOSErr(dp_CallFormatIOProc(ioReadFormat, IOParams, ioProc));
|
|
HUnLock(dataHandle);
|
|
|
|
Success(fi);
|
|
END; { dpTryToGetFormat }
|
|
|
|
|
|
|
|
|
|
|
|
{------------- dpPubRemoveLock -------------}
|
|
FUNCTION dpPubRemoveLock(thePubCB: PubCBHandle): OSErr;
|
|
VAR
|
|
PBH: HParamBlockRec;
|
|
anErr: OSErr;
|
|
BEGIN
|
|
dpPubRemoveLock := noErr;
|
|
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
{ is there a lock? }
|
|
IF rangeLockLen > 0 THEN
|
|
BEGIN
|
|
{ remove lock we put on file header }
|
|
WITH PBH DO
|
|
BEGIN
|
|
ioRefNum := fileRefNum;
|
|
ioReqCount := rangeLockLen;
|
|
ioPosMode := fsFromStart;
|
|
ioPosOffset := rangeLockStart;
|
|
END; {with}
|
|
anErr := PBUnLockRangeSync(@PBH);
|
|
dpPubRemoveLock := anErr;
|
|
{$IFC qExtraDebug }
|
|
IF anErr <> noErr
|
|
THEN DebugStr('dpPubRemoveLock: unlock returned unexpected err');
|
|
{$ENDC }
|
|
END; {if}
|
|
END; {with}
|
|
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
rangeLockStart := 0;
|
|
rangeLockLen := 0;
|
|
END; {with}
|
|
|
|
END; { dpPubRemoveLock }
|
|
|
|
|
|
|
|
{------------- dpPubAddLock -------------}
|
|
FUNCTION dpPubAddLock(thePubCB: PubCBHandle; lockStart, lockLen: INTEGER): OSErr;
|
|
VAR
|
|
PBH: HParamBlockRec;
|
|
lockErr: OSErr;
|
|
BEGIN
|
|
lockErr := noErr;
|
|
{$IFC qRangeCheck}
|
|
IF lockLen = 0
|
|
THEN DebugStr('dpPubAddLock: no lock to add');
|
|
IF thePubCB^^.rangeLockLen <> 0
|
|
THEN DebugStr('dpPubAddLock: a lock already exists;g');
|
|
{$ENDC}
|
|
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
{ is there a lock? }
|
|
IF rangeLockLen = 0 THEN
|
|
BEGIN
|
|
{ try to lock the requested range }
|
|
WITH PBH DO
|
|
BEGIN
|
|
ioRefNum := fileRefNum;
|
|
ioReqCount := lockLen;
|
|
ioPosMode := fsFromStart;
|
|
ioPosOffset := lockStart;
|
|
END; {with}
|
|
lockErr := PBLockRangeSync(@PBH);
|
|
|
|
IF lockErr = noErr THEN
|
|
BEGIN
|
|
{ if succesful, record it in control block }
|
|
rangeLockStart := lockStart;
|
|
rangeLockLen := lockLen;
|
|
END; {if}
|
|
END; {if}
|
|
END; {with}
|
|
|
|
dpPubAddLock := lockErr;
|
|
END; { dpPubAddLock }
|
|
|
|
|
|
|
|
{------------- dpFindPublisher -------------}
|
|
FUNCTION dpFindPublisher(thePubCB: PubCBHandle; canAskUser: BOOLEAN;
|
|
VAR publisherSectionH: SectionHandle;
|
|
VAR publisherApplication: AppRefNum;
|
|
VAR publisherDoc: FSSpec;
|
|
VAR theSectionID: LONGINT): OSErr;
|
|
{
|
|
,(publisherDoc, publisherDoc), xor (publisherDoc).
|
|
publisherSectionH is not NIL is the first case.
|
|
theSectionID is non-zero is the second case.
|
|
xor (publisherDoc, publisherDoc). publisherSectionH is not NIL is the first case.
|
|
|
|
Note: currently no one calls this with canAskUser=FALSE. The parameter
|
|
is here in case this is ever used for "preflighting" of finding the publisher.
|
|
|
|
}
|
|
VAR
|
|
publisherAlias: AliasHandle;
|
|
dummyFormat: FormatType;
|
|
tempPtr: pLongint;
|
|
aliasCount: INTEGER;
|
|
needsUpdate: BOOLEAN;
|
|
resolveErr: OSErr;
|
|
openErr: OSErr;
|
|
PBH: HParamBlockRec;
|
|
rfi: FailInfo;
|
|
|
|
PROCEDURE dpLookForRegisteredPublisher(sectionH: SectionHandle; inApp: AppRefNum);
|
|
BEGIN
|
|
IF sectionH^^.controlBlock = Handle(thePubCB) THEN
|
|
BEGIN
|
|
publisherSectionH := sectionH;
|
|
publisherApplication := inApp;
|
|
END; {if}
|
|
END; { dpLookForRegisteredPublisher }
|
|
|
|
BEGIN
|
|
dpFindPublisher := noErr;
|
|
publisherSectionH := NIL;
|
|
|
|
{ see if we already know publisher, if so we are done }
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
IF publisher <> NIL THEN
|
|
BEGIN
|
|
{ easy case, publisher known }
|
|
publisherSectionH := publisher;
|
|
publisherApplication := publisherApp;
|
|
EXIT(dpFindPublisher);
|
|
END; {if}
|
|
END; {with}
|
|
|
|
{ try using alias in edition file to find publisher }
|
|
IF NOT IsFailure(rfi, resolveErr) THEN { catch any failures properly }
|
|
BEGIN
|
|
FailOSErr(dpResolveAliasToPublisher(thePubCB, canAskUser, publisherDoc, theSectionID));
|
|
Success(rfi);
|
|
END;
|
|
|
|
{ if we found publisher via alias then we are done }
|
|
IF resolveErr = noErr
|
|
THEN EXIT(dpFindPublisher);
|
|
|
|
{ next possible way to find publisher is to check all currently registered sections }
|
|
IF thePubCB^^.publisherCount > 0 THEN
|
|
BEGIN
|
|
{ so scan all register sections }
|
|
dpForEachSectionDo(dpLookForRegisteredPublisher);
|
|
EXIT(dpFindPublisher);
|
|
END;
|
|
|
|
{ last way to find publisher is if there was no alias and no registered publisher }
|
|
IF resolveErr <> noTypeErr THEN FailOSErr(resolveErr); {noTypeErr => no alias in edition file }
|
|
|
|
{ #### note: the rest of this code needs to be fixed. This was written }
|
|
{ back when no alias meant that the edition was a "publisherless-edition" }
|
|
{ (that is a stand alone file. Opening a "publisherless-edition" launches an }
|
|
{ app which can edit it.) Now, the existance of a 'doev' format has that meaning. }
|
|
{ The following code should be replaced with a check for a 'doev' format }
|
|
{ (by calling GetStandardFormats and looking at the formats list.) If there }
|
|
{ is a 'doev' format, this routine should return the edition file as the }
|
|
{ publisherDoc (i.e. keep the BlockMove). }
|
|
|
|
{ assume app knows what it is doing (by not putting an alias }
|
|
{ in edition) and send it an odoc event to open edition }
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
IF bTST({thePubCB^^.}volumeInfo, bHasOpenDeny) THEN
|
|
BEGIN
|
|
{ This is a shared volume, so the publisher could be in }
|
|
{ an untitled document on another machine. If that is the }
|
|
{ case, we don't want to open edition here. }
|
|
|
|
{ We will try to open the edition for writing, if that fails }
|
|
{ then there is a publisher open for it somewhere. }
|
|
|
|
{ If edition file is already open, we can't check so just fail. }
|
|
IF {thePubCB^^.}fileRefNum <> kClosedFileRefNum THEN FailOSErr(fBsyErr);
|
|
|
|
{ try to open }
|
|
FailOSErr(FSpOpenDeny(info.container.theFile, dmRequestWritePerm+dmDenyOtherWriters, PBH.ioRefNum));
|
|
|
|
{ we could open it, so close it }
|
|
IgnoreOSErr(PBCloseSync(@PBH));
|
|
END; {if}
|
|
|
|
{ return edition file as publisher, and sectionID = 0 }
|
|
BlockMove(@info.container.theFile, @publisherDoc, SizeOf(FSSpec));
|
|
theSectionID := 0;
|
|
END; {with}
|
|
|
|
END; { dpFindPublisher }
|
|
|
|
|
|
|
|
{------------- dpResolveAliasToPublisher -------------}
|
|
FUNCTION dpResolveAliasToPublisher(thePubCB: PubCBHandle; canAskUser: BOOLEAN;
|
|
VAR publisherDoc: FSSpec;
|
|
VAR theSectionID: LONGINT): OSErr;
|
|
VAR
|
|
publisherAlias: AliasHandle;
|
|
dummyFormat: FormatType;
|
|
tempPtr: pLongint;
|
|
aliasCount: INTEGER;
|
|
needsUpdate: BOOLEAN;
|
|
rulesMask: LONGINT;
|
|
fi: FailInfo;
|
|
BEGIN
|
|
DoNotPutInRegister(@publisherAlias);
|
|
|
|
IF IsFailure(fi, dpResolveAliasToPublisher) THEN
|
|
BEGIN
|
|
IF publisherAlias <> NIL THEN DisposHandle(Handle(publisherAlias));
|
|
EXIT(dpResolveAliasToPublisher);
|
|
END;
|
|
|
|
{ try to resolve alias to publisher doc }
|
|
Handle(publisherAlias) := NewHandle(100); { dp_GetStandardFormats will resize as needed }
|
|
FailOSErr(MemError);
|
|
|
|
FailOSErr(dp_GetStandardFormats(thePubCB^^.info.container, dummyFormat, NIL, Handle(publisherAlias), NIL));
|
|
|
|
{ snag sectionID of publisher from end of alias }
|
|
tempPtr := pLongint(pLongint(publisherAlias)^ + publisherAlias^^.aliasSize);
|
|
theSectionID := tempPtr^ ;
|
|
|
|
{ resolve the alias }
|
|
aliasCount := 1;
|
|
IF canAskUser
|
|
THEN rulesMask := kARMsearch+kARMsearchRelFirst+kARMmountVol
|
|
ELSE rulesMask := kARMsearch+kARMsearchRelFirst+kARMnoUI;
|
|
{###? add a filter for documents only }
|
|
FailOSErr(MatchAlias(@thePubCB^^.info.container.theFile, { fromFile }
|
|
rulesMask, { rulesMask }
|
|
publisherAlias, { alias }
|
|
aliasCount, { VAR aliasCount }
|
|
@publisherDoc, { buffer }
|
|
needsUpdate, { VAR needsUpdate }
|
|
NIL, { aliasFilter }
|
|
NIL)); { yourDataPtr }
|
|
|
|
{ free up the temp alias }
|
|
DisposHandle(Handle(publisherAlias));
|
|
|
|
Success(fi); { <37> }
|
|
END; { dpResolveAliasToPublisher }
|
|
|
|
|
|
{================================ standard routines ===================================}
|
|
|
|
|
|
{------------- dpStandardSubscriberOpen -------------}
|
|
FUNCTION dpStandardSubscriberOpen(VAR info: EditionInfoRecord;
|
|
sectionH: SectionHandle;
|
|
VAR ioRefNum: LONGINT;
|
|
VAR ioProc: FormatIOProcPtr): OSErr;
|
|
VAR
|
|
thePubCB: PubCBHandle;
|
|
thisApp: AppRefNum;
|
|
PBH: HParamBlockRec;
|
|
PBF: FCBPBRec;
|
|
randomRangeLock:LONGINT;
|
|
lockErr: OSErr;
|
|
i: INTEGER;
|
|
justOpened: BOOLEAN;
|
|
mapJustLoaded: BOOLEAN;
|
|
addedLock: BOOLEAN;
|
|
editionName: Str63;
|
|
temp: LONGINT;
|
|
fi: FailInfo;
|
|
BEGIN
|
|
{ initialize state }
|
|
DoNotPutInRegister(@justOpened);
|
|
DoNotPutInRegister(@mapJustLoaded);
|
|
DoNotPutInRegister(@addedLock);
|
|
DoNotPutInRegister(@thePubCB);
|
|
mapJustLoaded := FALSE;
|
|
justOpened := FALSE;
|
|
addedLock := FALSE;
|
|
thePubCB := NIL;
|
|
|
|
{ need a failure handler to undo, if failed half way through }
|
|
IF IsFailure(fi, dpStandardSubscriberOpen) THEN
|
|
BEGIN
|
|
IF NOT justOpened THEN
|
|
BEGIN
|
|
IF addedLock THEN IgnoreOSErr(dpPubRemoveLock(thePubCB));
|
|
END
|
|
ELSE IgnoreOSErr(dpPubCloseFile(thePubCB, NOT kFlush));
|
|
IF mapJustLoaded THEN IgnoreOSErr(dpPubReleaseMap(thePubCB));
|
|
IF thePubCB <> NIL {&} THEN IF sectionH = NIL
|
|
THEN IgnoreOSErr(dpReleasePubCB(thePubCB, thisApp));
|
|
EXIT(dpStandardSubscriberOpen);
|
|
END;
|
|
|
|
FailOSErr(dp_GetCurrentAppRefNum(thisApp));
|
|
|
|
{ get the control block through the section or by filename }
|
|
IF sectionH = NIL THEN
|
|
BEGIN
|
|
FailOSErr(dpRequestPubCB(info.container.theFile, thisApp,
|
|
kCheckExisting, NOT kCanAllocateNew, thePubCB));
|
|
END ELSE
|
|
BEGIN
|
|
thePubCB := PubCBHandle(sectionH^^.controlBlock);
|
|
END;
|
|
|
|
{ load edition file maps, if not already so }
|
|
IF thePubCB^^.allocMap = NIL THEN
|
|
BEGIN
|
|
{ make sure the file is open }
|
|
IF thePubCB^^.openMode = dmNotOpen THEN
|
|
BEGIN
|
|
FailOSErr(dpPubOpenFile(thePubCB, stSubscriber));
|
|
justOpened := TRUE;
|
|
END;
|
|
|
|
{ on shared volumes we have to range lock a byte in the header }
|
|
{ to prevent publisher from writing }
|
|
IF bTST(thePubCB^^.volumeInfo, bHasOpenDeny) THEN
|
|
BEGIN
|
|
{ only need to add a lock if we don't already have one }
|
|
IF thePubCB^^.rangeLockLen = 0 THEN
|
|
BEGIN
|
|
{ first try random lock }
|
|
randomRangeLock := kSubscriberRangeLockStart + bAND(Random, kSubscriberRangeLockMask);
|
|
|
|
{ try to lock a bit, to let the publisher know we are here }
|
|
lockErr := dpPubAddLock(thePubCB, randomRangeLock, 1);
|
|
|
|
IF lockErr = fLckdErr THEN
|
|
BEGIN
|
|
{ if range lock failed, then chances are it is because }
|
|
{ the publisher is writing. To test this we try to read }
|
|
{ a bit from the header, which publishers have locked. }
|
|
PBH.ioRefNum := thePubCB^^.fileRefNum;
|
|
PBH.ioBuffer := @temp;
|
|
PBH.ioReqCount := 2;
|
|
PBH.ioPosMode := fsFromStart;
|
|
PBH.ioPosOffset := 0;
|
|
FailOSErr(PBReadSync(@PBH)); { if publisher is writing, this will fail }
|
|
END; {if}
|
|
|
|
{ publisher is not writing, so it is rotton luck that we }
|
|
{ happened to choose the same offset as another subscriber }
|
|
FOR i := kSubscriberLockRetries DOWNTO 0 DO
|
|
BEGIN
|
|
{ if unexpected error then FailOSErr out }
|
|
IF lockErr <> fLckdErr
|
|
THEN FailOSErr(lockErr);
|
|
|
|
{ try next byte, but wrap around if needed }
|
|
randomRangeLock := kSubscriberRangeLockStart + bAND(randomRangeLock+1, kSubscriberRangeLockMask);
|
|
lockErr := dpPubAddLock(thePubCB, randomRangeLock, 1);
|
|
|
|
{ jump out as soon as lock succeeds }
|
|
IF lockErr = noErr THEN LEAVE; {for}
|
|
END; {for}
|
|
{ fail if last try did not succeed }
|
|
FailOSErr(lockErr);
|
|
addedLock := TRUE;
|
|
END; {if}
|
|
|
|
{ check FCB to see if file has been renamed }
|
|
{ this can happen if lock gets in between time publisher closes and deletes }
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
{ file is open, can track down access path to get info }
|
|
PBF.ioNamePtr := @editionName; { put current name here }
|
|
PBF.ioRefNum := {thePubCB^^.}fileRefNum;
|
|
PBF.ioFCBIndx := 0;
|
|
FailOSErr(PBGetFCBInfoSync(@PBF));
|
|
IF PBF.ioFCBParID <> 0 THEN { only do this for HFS volumes }
|
|
BEGIN
|
|
{ if the file name has changed, then publisher is busy writing }
|
|
IF NOT dpSameBytes(@editionName[0], @{thePubCB^^.}info.container.theFile.name[0], LENGTH(editionName)+1)
|
|
THEN FailOSErr(fLckdErr);
|
|
END; {if}
|
|
END; {with}
|
|
|
|
END; {if}
|
|
|
|
{ load map }
|
|
FailOSErr(dpPubLoadMap(thePubCB, stSubscriber));
|
|
mapJustLoaded := TRUE;
|
|
END; {if}
|
|
|
|
{ bump this apps open count for this PubCB }
|
|
FailOSErr(dpPubCBbumpUsageCount(thePubCB^^.usageInfo, thisApp, kCanGrowUsage, 0, 1));
|
|
|
|
ioRefNum := ORD(thePubCB);
|
|
ioProc := FormatIOProcPtr(kStandardFormatIOProcPtr);
|
|
|
|
Success(fi);
|
|
END; { dpStandardSubscriberOpen }
|
|
|
|
|
|
|
|
|
|
{------------- dpStandardSubscriberClose -------------}
|
|
FUNCTION dpStandardSubscriberClose(sectionH: SectionHandle;
|
|
ioRefNum: LONGINT;
|
|
ioProc: FormatIOProcPtr): OSErr;
|
|
VAR
|
|
thePubCB: PubCBHandle;
|
|
thisApp: AppRefNum;
|
|
anErr: OSErr;
|
|
BEGIN
|
|
thePubCB := PubCBHandle(ioRefNum);
|
|
FailOSErr(dp_GetCurrentAppRefNum(thisApp));
|
|
|
|
{ dec this apps open count for this PubCB }
|
|
FailOSErr(dpPubCBbumpUsageCount(thePubCB^^.usageInfo, thisApp, NOT kCanGrowUsage, 0, -1));
|
|
|
|
{ stop IO, if this is last section that is doing any IO }
|
|
IF thePubCB^^.usageInfo^^.totalIOCount = 0 THEN
|
|
BEGIN
|
|
{ dump allocation map and formats list }
|
|
FailOSErr(dpPubReleaseMap(thePubCB));
|
|
|
|
IF bTST(thePubCB^^.openMode, dmRequestWritePermBit) THEN
|
|
BEGIN
|
|
{ a publisher also has the file open, so don't close it, }
|
|
{ just remove the subscriber's lock. }
|
|
FailOSErr(dpPubRemoveLock(thePubCB));
|
|
END ELSE
|
|
BEGIN
|
|
{ no publisher, so close and release lock }
|
|
FailOSErr(dpPubCloseFile(thePubCB, NOT kFlush)); { this will also remove the range lock }
|
|
END;
|
|
END; {if}
|
|
|
|
{ undo what we did in dpStandardSubscriberOpen }
|
|
IF sectionH = NIL
|
|
THEN FailOSErr(dpReleasePubCB(thePubCB, thisApp));
|
|
|
|
dpStandardSubscriberClose := noErr;
|
|
END; { dpStandardSubscriberClose }
|
|
|
|
|
|
|
|
{------------- dpStandardPublisherOpen -------------}
|
|
FUNCTION dpStandardPublisherOpen(sectionH: SectionHandle;
|
|
fdCreator: OSType;
|
|
document: FSSpecPtr;
|
|
VAR ioRefNum: LONGINT;
|
|
VAR ioProc: FormatIOProcPtr): OSErr;
|
|
VAR
|
|
thisApp: AppRefNum;
|
|
tempEdtnFile: FSSpec;
|
|
thePubCB: PubCBHandle;
|
|
newPubCB: PubCBHandle;
|
|
aliasH: AliasHandle;
|
|
tempPtr: pLongint;
|
|
fileCreated: BOOLEAN;
|
|
ioCountBumped: BOOLEAN;
|
|
PBH: HParamBlockRec;
|
|
ignoreLong: LONGINT;
|
|
i: INTEGER;
|
|
lockErr: OSErr;
|
|
fi: FailInfo;
|
|
BEGIN
|
|
DoNotPutInRegister(@fileCreated);
|
|
DoNotPutInRegister(@ioCountBumped);
|
|
DoNotPutInRegister(@newPubCB);
|
|
fileCreated := FALSE;
|
|
ioCountBumped := FALSE;
|
|
newPubCB := NIL;
|
|
|
|
{ set up failure handling }
|
|
IF isFailure(fi, dpStandardPublisherOpen) THEN
|
|
BEGIN
|
|
{ undo any thing we allocated, in reverse order of allocation }
|
|
IF ioCountBumped THEN
|
|
BEGIN
|
|
IgnoreOSErr(dpPubCBbumpUsageCount(newPubCB^^.usageInfo, thisApp, NOT kCanGrowUsage, 0, -1));
|
|
END;
|
|
IF newPubCB <> NIL THEN
|
|
BEGIN
|
|
sectionH^^.controlBlock := Handle(thePubCB);
|
|
IgnoreOSErr(dpReleasePubCB(newPubCB, thisApp));
|
|
END;
|
|
IF fileCreated THEN
|
|
BEGIN
|
|
IgnoreOSErr(FSpDelete(tempEdtnFile));
|
|
END; {if}
|
|
EXIT(dpStandardPublisherOpen);
|
|
END; {if}
|
|
|
|
FailOSErr(dp_GetCurrentAppRefNum(thisApp));
|
|
thePubCB := PubCBHandle(sectionH^^.controlBlock);
|
|
|
|
{ is this on a shared volume? }
|
|
IF bTST(thePubCB^^.volumeInfo, bHasOpenDeny) THEN
|
|
BEGIN
|
|
{ if subscriber/publisher already has a lock (is reading/writing), tell publisher it is inuse }
|
|
IF thePubCB^^.rangeLockLen > 0
|
|
THEN FailOSErr(fLckdErr);
|
|
|
|
{ range lock file header to prevent subscribers from opening for read }
|
|
{ and potentially preventing me from deleting old edition file when I am done }
|
|
FOR i := kPublisherLockRetries DOWNTO 0 DO
|
|
BEGIN
|
|
lockErr := dpPubAddLock(thePubCB, kPublisherRangeLockStart, kPublisherRangeLockLength);
|
|
|
|
{ jump out as soon as lock succeeds }
|
|
IF lockErr = noErr THEN LEAVE; {for}
|
|
|
|
{ if unexpected error then FailOSErr out }
|
|
IF lockErr <> fLckdErr THEN FailOSErr(lockErr);
|
|
|
|
{ wait a bit before retry }
|
|
Delay({numTicks}kTicksBetweenPublisherRetries, {VAR finalTicks}ignoreLong);
|
|
END; {for}
|
|
FailOSErr(lockErr);
|
|
END; {if}
|
|
|
|
{ create a temp-named edition file }
|
|
tempEdtnFile := thePubCB^^.info.container.theFile;
|
|
dpRandomFileName(tempEdtnFile.name);
|
|
FailOSErr(FSpCreate(tempEdtnFile, fdCreator, kUnknownEditionFileType, thePubCB^^.info.container.theFileScript));
|
|
fileCreated := TRUE;
|
|
|
|
{ create a PubCB for it }
|
|
FailOSErr(dpRequestPubCB(tempEdtnFile, thisApp, NOT kCheckExisting, kCanAllocateNew, newPubCB));
|
|
sectionH^^.controlBlock := Handle(newPubCB);
|
|
|
|
{ fill in the fields not done right by RequestPubCB }
|
|
WITH newPubCB^^ DO
|
|
BEGIN
|
|
oldPubCB := thePubCB;
|
|
publisher := sectionH; { same as thePubCB^^.publisher }
|
|
publisherApp := thisApp; { same as thePubCB^^.publisherApp }
|
|
END; {with}
|
|
|
|
{ initialize an empty map and formats list }
|
|
FailOSErr(dpPubLoadMap(newPubCB, stPublisher));
|
|
|
|
{ bump this apps open count for this PubCB }
|
|
FailOSErr(dpPubCBbumpUsageCount(newPubCB^^.usageInfo, thisApp, kCanGrowUsage, 0, 1));
|
|
ioCountBumped := TRUE;
|
|
|
|
{ create the "backward" link from the EditionFile to the PublisherDocument }
|
|
aliasH := NIL;
|
|
IF document <> NIL THEN
|
|
BEGIN
|
|
FailOSErr(NewAlias(@newPubCB^^.info.container.theFile, document^, aliasH));
|
|
{$IFC qCatchFailures}
|
|
IF aliasH^^.aliasSize <> GetHandleSize(Handle(aliasH))
|
|
THEN DebugStr('dp_OpenNewEdition: incorrect alias size');
|
|
{$ENDC}
|
|
|
|
{ add the sectionID of the publisher to the end of the alias }
|
|
SetHandleSize(Handle(aliasH), aliasH^^.aliasSize + SizeOf(LONGINT));
|
|
IF MemError = noErr THEN
|
|
BEGIN
|
|
tempPtr := pLongint(pLongint(aliasH)^ + aliasH^^.aliasSize);
|
|
tempPtr^ := sectionH^^.sectionID;
|
|
END; {if}
|
|
END; {if}
|
|
|
|
{ save off alias until close }
|
|
newPubCB^^.publisherAlias := aliasH;
|
|
|
|
ioRefNum := ORD4(newPubCB);
|
|
ioProc := FormatIOProcPtr(kStandardFormatIOProcPtr);
|
|
|
|
Success(fi);
|
|
END; { dpStandardPublisherOpen }
|
|
|
|
|
|
|
|
{------------- dpStandardPublisherClose -------------}
|
|
FUNCTION dpStandardPublisherClose(sectionH: SectionHandle;
|
|
success: BOOLEAN;
|
|
ioRefNum: LONGINT;
|
|
ioProc: FormatIOProcPtr): OSErr;
|
|
VAR
|
|
newPubCB: PubCBHandle;
|
|
orgPubCB: PubCBHandle;
|
|
thisApp: AppRefNum;
|
|
aliasH: AliasHandle;
|
|
IOParams: FormatIOParamBlock;
|
|
areSubscribers: BOOLEAN;
|
|
originalCrDate: TimeStamp;
|
|
newMdDate: TimeStamp;
|
|
foundIndex: INTEGER;
|
|
foundPtr: FormatPtr;
|
|
newType: OSType;
|
|
knownFormatsCount: INTEGER;
|
|
savedPublisherCount:INTEGER;
|
|
swapSucceeded: BOOLEAN;
|
|
fi: FailInfo;
|
|
|
|
FUNCTION FindStandardFormat(anEntryIndex: INTEGER; anEntryPtr: FormatPtr): BOOLEAN;
|
|
BEGIN
|
|
{ handle each case }
|
|
WITH anEntryPtr^ DO
|
|
BEGIN
|
|
IF LONGINT(theType) = LONGINT('PICT') THEN
|
|
BEGIN
|
|
IF knownFormatsCount = 0
|
|
THEN newType := kPICTEditionFileType;
|
|
knownFormatsCount := knownFormatsCount + 1;
|
|
END ELSE
|
|
IF LONGINT(theType) = LONGINT('TEXT') THEN
|
|
BEGIN
|
|
IF knownFormatsCount = 0
|
|
THEN newType := kTEXTEditionFileType;
|
|
knownFormatsCount := knownFormatsCount + 1;
|
|
END ELSE
|
|
IF LONGINT(theType) = LONGINT('snd ') THEN
|
|
BEGIN
|
|
IF knownFormatsCount = 0
|
|
THEN newType := ksndEditionFileType;
|
|
knownFormatsCount := knownFormatsCount + 1;
|
|
END;
|
|
END; {with}
|
|
FindStandardFormat := FALSE;
|
|
END; { FindStandardFormat }
|
|
|
|
|
|
PROCEDURE KillIOtoOldPubCB(aSection: SectionHandle; inApp: AppRefNum);
|
|
BEGIN
|
|
WITH aSection^^ DO
|
|
BEGIN
|
|
IF (SIOCBHandle(refNum) <> NIL) {&} THEN IF (PubCBHandle(controlBlock) = orgPubCB) THEN
|
|
BEGIN
|
|
SIOCBHandle(refNum)^^.ioProc := FormatIOProcPtr(kBogusFormatIOProcPtr);
|
|
IF dpPubCBbumpUsageCount(orgPubCB^^.usageInfo, inApp, NOT kCanGrowUsage, 0, -1) <> noErr THEN
|
|
BEGIN
|
|
{$IFC qRangeCheck }
|
|
DebugStr('dp_CloseEditionThatWasOpenForWriting.KillIOtoOldPubCB: could not close a subscriber.');
|
|
{$ENDC}
|
|
END;
|
|
END; {if}
|
|
END; {with}
|
|
END; { KillIOtoOldPubCB }
|
|
|
|
PROCEDURE dpSetMdCrDate(VAR PBC: CInfoPBRec);
|
|
VAR
|
|
flags: LONGINT;
|
|
BEGIN
|
|
PBC.ioFlMdDat := newMdDate;
|
|
PBC.ioFlCrDat := originalCrDate;
|
|
|
|
flags := PBC.ioFlFndrInfo.fdFlags;
|
|
IF knownFormatsCount > 1
|
|
THEN bSet(flags, kHasMultipleFormatsBit)
|
|
ELSE bClr(flags, kHasMultipleFormatsBit);
|
|
PBC.ioFlFndrInfo.fdFlags := flags;
|
|
END; { dpSetMdCrDate }
|
|
|
|
BEGIN
|
|
FailOSErr(dp_GetCurrentAppRefNum(thisApp));
|
|
newPubCB := PubCBHandle(ioRefNum);
|
|
orgPubCB := newPubCB^^.oldPubCB;
|
|
|
|
{ dec this apps IO count for this PubCB }
|
|
FailOSErr(dpPubCBbumpUsageCount(newPubCB^^.usageInfo, thisApp, NOT kCanGrowUsage, 0, -1));
|
|
|
|
{$IFC qRangeCheck }
|
|
IF newPubCB^^.usageInfo^^.totalIOCount <> 0
|
|
THEN DebugStr('dp_CloseEditionThatWasOpenForWriting: opened by someone other than Publisher ?!?!');
|
|
{$ENDC }
|
|
|
|
IF success THEN
|
|
BEGIN
|
|
{ retrieve the "backward" link from the EditionFile to the PublisherDocument }
|
|
aliasH := newPubCB^^.publisherAlias;
|
|
|
|
{ no alias means the app does not have a disk version of the document yet }
|
|
IF aliasH <> NIL THEN
|
|
BEGIN
|
|
{ create an 'alis' format }
|
|
IOParams.ioRefNum := ioRefNum;
|
|
IOParams.format := kPublisherDocAliasFormat;
|
|
FailOSErr(dp_CallFormatIOProc(ioNewFormat, IOParams, ioProc));
|
|
|
|
{ write the 'alis' format }
|
|
HLock(Handle(aliasH));
|
|
{IOParams.refNum already set }
|
|
{IOParams.format already set }
|
|
{IOParams.formatIndex already set }
|
|
IOParams.offset := 0;
|
|
IOParams.buffPtr := Ptr(aliasH^);
|
|
IOParams.buffLen := GetHandleSize(Handle(aliasH));
|
|
FailOSErr(dp_CallFormatIOProc(ioWriteFormat, IOParams, ioProc));
|
|
|
|
{ dispose of backward link }
|
|
newPubCB^^.publisherAlias := NIL;
|
|
DisposHandle(Handle(aliasH));
|
|
END; {if}
|
|
|
|
{ flush info to disk }
|
|
FailOSErr(dpPubFlushFile(newPubCB));
|
|
|
|
{ figure out fdType for new edition file }
|
|
newType := kUnknownEditionFileType;
|
|
knownFormatsCount := 0;
|
|
IF dpFindElement(newPubCB^^.formats, 0, FindStandardFormat, foundIndex, foundPtr) THEN;
|
|
|
|
{ set new finder type based of first known format and set flag if multiple known }
|
|
newPubCB^^.info.fdType := newType;
|
|
|
|
{ try to dump new map and format list }
|
|
areSubscribers := (orgPubCB^^.usageInfo^^.totalUseCount > 1);
|
|
IF NOT areSubscribers THEN
|
|
BEGIN
|
|
{ no current subscribers, so can free up new map and formats }
|
|
FailOSErr(dpPubReleaseMap(newPubCB));
|
|
END;
|
|
|
|
{ if orgPubCB file has map in memory, free it (maybe blowing up subscribers) }
|
|
IF orgPubCB^^.allocMap <> NIL THEN
|
|
BEGIN
|
|
IF areSubscribers THEN dpForEachSectionDo(KillIOtoOldPubCB);
|
|
FailOSErr(dpPubReleaseMap(orgPubCB));
|
|
END; {if}
|
|
|
|
{ save create and modification date }
|
|
IF sectionH <> NIL
|
|
THEN newMdDate := sectionH^^.mdDate
|
|
ELSE newMdDate := newPubCB^^.info.mdDate;
|
|
originalCrDate := orgPubCB^^.info.crDate;
|
|
|
|
{ swap data from new into old pub file, delete new pub file }
|
|
{ if swap fails, we keep orgPubCB as is, and delete newPubCB }
|
|
swapSucceeded := (dpSwapAndDelete(orgPubCB, newPubCB) = noErr);
|
|
|
|
{ blockmove contents of newPubCB over orgPubCB, to keep subscribers }
|
|
{ but, keep old info.container and publisherCount }
|
|
WITH orgPubCB^^ DO
|
|
BEGIN
|
|
IF swapSucceeded THEN
|
|
BEGIN
|
|
savedPublisherCount := {orgPubCB^^.}publisherCount;
|
|
BlockMove( @newPubCB^^.pubCNodeID, {from}
|
|
@{orgPubCB^^.}pubCNodeID, {to}
|
|
ORD(@{orgPubCB^^.}info.container) - ORD(@{orgPubCB^^.}pubCNodeID) {len});
|
|
{orgPubCB^^.}publisherCount := savedPublisherCount;
|
|
{orgPubCB^^.}oldPubCB := NIL;
|
|
|
|
{ fix up mod dates to make polling more efficient }
|
|
lastVolMod := {orgPubCB^^.}info.mdDate;
|
|
lastDirMod := {orgPubCB^^.}info.mdDate;
|
|
|
|
{ set new edition's mdDate and crDate }
|
|
{orgPubCB^^.}info.crDate := originalCrDate;
|
|
{orgPubCB^^.}info.mdDate := newMdDate;
|
|
FailOSErr(dpTweakCatInfo({orgPubCB^^.}info.container.theFile, dpSetMdCrDate));
|
|
END;
|
|
|
|
{ make this section the publisher for orgPubCB }
|
|
{orgPubCB^^.}publisher := sectionH;
|
|
WITH sectionH^^ DO
|
|
BEGIN
|
|
PubCBHandle({sectionH^^.}controlBlock) := orgPubCB;
|
|
publisherKind := {sectionH^^.}kind;
|
|
END; {with}
|
|
END; {with}
|
|
|
|
{ remove newPubCB }
|
|
WITH newPubCB^^ DO
|
|
BEGIN
|
|
{ the refnum in newPubCB has been copied into orgPubCB, but DisposePubCB }
|
|
{ does a sanity check that the fileRefNum says the file is closed, so fake it }
|
|
{newPubCB^^.}fileRefNum := kClosedFileRefNum;
|
|
IgnoreOSErr(dpPubCBbumpUsageCount({newPubCB^^.}usageInfo, thisApp, NOT kCanGrowUsage, -1, 0));
|
|
END; {with}
|
|
IgnoreOSErr(dpDisposePubCB(newPubCB));
|
|
END ELSE
|
|
BEGIN
|
|
{ unsuccessful write, want to roll back to state before OpenNewEdition }
|
|
{ note that since we are closing, we ignore all errors and plow on ahead }
|
|
|
|
{ remove lock }
|
|
IgnoreOSErr(dpPubRemoveLock(newPubCB));
|
|
|
|
{ point section back to old PubCB }
|
|
PubCBHandle(sectionH^^.controlBlock) := orgPubCB;
|
|
|
|
{ dump map built so far and close temp file }
|
|
IgnoreOSErr(dpPubReleaseMap(newPubCB));
|
|
IgnoreOSErr(dpPubCloseFile(newPubCB, NOT kFlush));
|
|
|
|
{ delete temp file, since it won't be used }
|
|
IgnoreOSErr(FSpDelete(newPubCB^^.info.container.theFile));
|
|
|
|
{ remove newPubCB }
|
|
IgnoreOSErr(dpDisposePubCB(newPubCB));
|
|
END; {if success}
|
|
|
|
dpStandardPublisherClose := noErr;
|
|
END; { dpStandardPublisherClose }
|
|
|
|
|
|
|
|
{------------- dpStandardCanSubscribe -------------}
|
|
FUNCTION dpStandardCanSubscribe(VAR info: EditionInfoRecord; formatsMask: SignedByte; hasMultipleFormats: BOOLEAN): OSErr;
|
|
VAR
|
|
hasFormat: SignedByte;
|
|
formatsH: Handle;
|
|
ignoreType: FormatType;
|
|
getErr: OSErr;
|
|
ignoreRem: INTEGER;
|
|
i: INTEGER;
|
|
p: FormatPtr;
|
|
BEGIN
|
|
{ assume failure }
|
|
dpStandardCanSubscribe := noTypeErr;
|
|
|
|
{ can only subscribe to whole files of type: edtp, edtt, edts, or edtu }
|
|
IF info.container.thePart <> kPartsNotUsed
|
|
THEN EXIT(dpStandardCanSubscribe);
|
|
|
|
{ ### grandfather in all 'publ' s}
|
|
IF LONGINT(info.fdType) = LONGINT('publ') THEN
|
|
BEGIN
|
|
dpStandardCanSubscribe := noErr;
|
|
EXIT(dpStandardCanSubscribe);
|
|
END;
|
|
|
|
{ map finder type to appropriate mask bit or fail }
|
|
IF LONGINT(info.fdType) = LONGINT(kPICTEditionFileType) THEN hasFormat := kPICTformatMask
|
|
ELSE IF LONGINT(info.fdType) = LONGINT(kTEXTEditionFileType) THEN hasFormat := kTEXTformatMask
|
|
ELSE IF LONGINT(info.fdType) = LONGINT(ksndEditionFileType) THEN hasFormat := ksndformatMask
|
|
ELSE IF LONGINT(info.fdType) = LONGINT(kUnknownEditionFileType) THEN hasFormat := 0
|
|
ELSE IF LONGINT(info.fdType) = LONGINT('edtP') THEN hasFormat := kPICTformatMask { ### grandfather in edtP }
|
|
ELSE IF LONGINT(info.fdType) = LONGINT('edtT') THEN hasFormat := kTEXTformatMask { ### grandfather in edtT }
|
|
ELSE EXIT(dpStandardCanSubscribe);
|
|
|
|
{ return if bit corrosponding to finder type is in mask }
|
|
IF bAND(formatsMask, hasFormat) <> 0 THEN
|
|
BEGIN
|
|
{ the mask says it can handle this edition file }
|
|
dpStandardCanSubscribe := noErr;
|
|
EXIT(dpStandardCanSubscribe);
|
|
END;
|
|
|
|
{ bit not in mask, so need to check secondary formats }
|
|
{ hasMultipleFormats flag is state of kHasMultipleFormatsBit flag }
|
|
IF hasMultipleFormats {&} THEN IF bAND(formatsMask,kPICTformatMask+kTEXTformatMask+ksndformatMask) <> 0 THEN
|
|
BEGIN
|
|
formatsH := NewHandle(0);
|
|
getErr := GetStandardFormats(info.container, ignoreType, NIL, NIL, formatsH);
|
|
IF getErr = noErr THEN
|
|
BEGIN
|
|
UnSignedDivide(GetHandleSize(formatsH), SizeOf(Format), i, ignoreRem);
|
|
{$IFC qRangeCheck }
|
|
IF ignoreRem <> 0 THEN DebugStr('dpStandardCanSubscribe: bad fmts handle length.');
|
|
{$ENDC }
|
|
p := FormatPtr(formatsH^);
|
|
REPEAT
|
|
WITH p^ DO
|
|
BEGIN
|
|
IF LONGINT(theType) = LONGINT('PICT') {&} THEN IF bAND(formatsMask, kPICTformatMask) <> 0
|
|
THEN dpStandardCanSubscribe := noErr;
|
|
IF LONGINT(theType) = LONGINT('TEXT') {&} THEN IF bAND(formatsMask, kTEXTformatMask) <> 0
|
|
THEN dpStandardCanSubscribe := noErr;
|
|
IF LONGINT(theType) = LONGINT('snd ') {&} THEN IF bAND(formatsMask, ksndformatMask) <> 0
|
|
THEN dpStandardCanSubscribe := noErr;
|
|
END; {with}
|
|
p := FormatPtr( ORD(p) + SizeOf(Format) );
|
|
i := i - 1;
|
|
UNTIL i <= 0;
|
|
DisposHandle(formatsH);
|
|
END
|
|
ELSE dpStandardCanSubscribe := getErr;
|
|
END; {if}
|
|
|
|
END; { dpStandardCanSubscribe }
|
|
|
|
|
|
|
|
{------------- dpStandardOpener -------------}
|
|
FUNCTION dpStandardOpener(selector: EditionOpenerVerb; VAR PB: EditionOpenerParamBlock): OSErr;
|
|
BEGIN
|
|
WITH PB DO
|
|
BEGIN
|
|
CASE selector OF
|
|
eoOpen:
|
|
dpStandardOpener := dpStandardSubscriberOpen(info, sectionH, ioRefNum, ioProc);
|
|
|
|
eoClose:
|
|
dpStandardOpener := dpStandardSubscriberClose(sectionH, ioRefNum, ioProc);
|
|
|
|
eoOpenNew:
|
|
dpStandardOpener := dpStandardPublisherOpen(sectionH, fdCreator, document, ioRefNum, ioProc);
|
|
|
|
eoCloseNew:
|
|
dpStandardOpener := dpStandardPublisherClose(sectionH, success, ioRefNum, ioProc);
|
|
|
|
eoCanSubscribe:
|
|
dpStandardOpener := dpStandardCanSubscribe(info, formatsMask, success);
|
|
|
|
OTHERWISE
|
|
dpStandardOpener := paramErr;
|
|
END; {case}
|
|
END; {with}
|
|
END; { dpStandardOpener }
|
|
|
|
|
|
{================================ public routines ===================================}
|
|
|
|
|
|
|
|
{------------- dp_CreateEditionContainerFile -------------}
|
|
FUNCTION dp_CreateEditionContainerFile(editionFile: FSSpec;
|
|
fdCreator: OSType;
|
|
editionFileNameScript: INTEGER): OSErr;
|
|
BEGIN
|
|
dp_CreateEditionContainerFile := FSpCreate(editionFile, fdCreator,
|
|
kUnknownEditionFileType, editionFileNameScript);
|
|
END; { dp_CreateEditionContainerFile }
|
|
|
|
|
|
|
|
{------------- dp_DeleteEditionContainerFile -------------}
|
|
FUNCTION dp_DeleteEditionContainerFile(editionFile: FSSpec): OSErr;
|
|
VAR
|
|
thePubCB: PubCBHandle;
|
|
thisApp: AppRefNum;
|
|
deleteIt: Boolean;
|
|
fi: FailInfo;
|
|
|
|
PROCEDURE KillIOtoThePubCB(aSection: SectionHandle; inApp: AppRefNum);
|
|
BEGIN
|
|
WITH aSection^^ DO
|
|
BEGIN
|
|
IF (SIOCBHandle(refNum) <> NIL) {&} THEN IF (PubCBHandle(controlBlock) = thePubCB) THEN
|
|
BEGIN
|
|
SIOCBHandle(refNum)^^.ioProc := FormatIOProcPtr(kBogusFormatIOProcPtr);
|
|
IF dpPubCBbumpUsageCount(thePubCB^^.usageInfo, inApp, NOT kCanGrowUsage, 0, -1) <> noErr THEN
|
|
BEGIN
|
|
{$IFC qRangeCheck }
|
|
DebugStr('dp_DeleteEditionContainerFile.KillIOtoThePubCB: could not close a subscriber.');
|
|
{$ENDC}
|
|
END;
|
|
END; {if}
|
|
END; {with}
|
|
END; { KillIOtoThePubCB }
|
|
|
|
BEGIN
|
|
{ assume we will delete the edition file }
|
|
deleteIt := TRUE;
|
|
|
|
{ top level functions must set up a failure handler }
|
|
IF IsFailure(fi, dp_DeleteEditionContainerFile)
|
|
THEN EXIT(dp_DeleteEditionContainerFile);
|
|
|
|
FailOSErr(dp_GetCurrentAppRefNum(thisApp));
|
|
|
|
{ see if it is in use by any sections }
|
|
IF dpRequestPubCB(editionFile, thisApp, kCheckExisting,
|
|
NOT kCanAllocateNew, thePubCB) = noErr THEN
|
|
BEGIN
|
|
{ are there are publishers? }
|
|
IF thePubCB^^.publisherCount = 0 THEN
|
|
BEGIN
|
|
{ is some subscribers are doing I/O then stop them }
|
|
IF thePubCB^^.usageInfo^^.totalIOCount > 0 THEN
|
|
BEGIN
|
|
dpForEachSectionDo(KillIOtoThePubCB);
|
|
|
|
{ if original pubCB file is open, close it }
|
|
IF thePubCB^^.openMode <> dmNotOpen THEN
|
|
BEGIN
|
|
{$IFC qRangeCheck }
|
|
IF thePubCB^^.usageInfo^^.totalIOCount <> 0
|
|
THEN DebugStr('dp_DeleteEditionContainerFile: could not kill off all subscribers.');
|
|
{$ENDC }
|
|
IgnoreOSErr(dpPubCloseFile(thePubCB, NOT kFlush));
|
|
END; {if}
|
|
END; {if}
|
|
|
|
{ mark file missing in control block }
|
|
thePubCB^^.fileMissing := TRUE;
|
|
END
|
|
ELSE deleteIt := FALSE;
|
|
|
|
{ now release the control block we requested }
|
|
IgnoreOSErr(dpReleasePubCB(thePubCB, thisApp));
|
|
END; {if}
|
|
|
|
IF deleteIt THEN
|
|
BEGIN
|
|
{ delete pub file }
|
|
FailOSErr(FSpDelete(editionFile));
|
|
END;
|
|
|
|
Success(fi);
|
|
END; { dp_DeleteEditionContainerFile }
|
|
|
|
|
|
|
|
{------------- dp_OpenEdition -------------}
|
|
FUNCTION dp_OpenEdition(sectionH: SectionHandle; VAR sectCB: SIOCBHandle): OSErr;
|
|
VAR
|
|
thePubCB: PubCBHandle;
|
|
thisApp: AppRefNum;
|
|
openerParams: EditionOpenerParamBlock;
|
|
fi: FailInfo;
|
|
BEGIN
|
|
sectCB := NIL;
|
|
|
|
{ set up failure handling }
|
|
IF isFailure(fi, dp_OpenEdition) THEN
|
|
BEGIN
|
|
IF sectCB <> NIL
|
|
THEN DisposHandle(Handle(sectCB));
|
|
sectCB := NIL;
|
|
SIOCBHandle(sectionH^^.refNum) := NIL;
|
|
EXIT(dp_OpenEdition);
|
|
END; {if}
|
|
|
|
{$IFC qDebug }
|
|
FailOSErr(dpCheckSection(sectionH));
|
|
{$ENDC }
|
|
|
|
{ make sure section can read }
|
|
IF NOT bTST(sectionH^^.kind, kCanReadEditionsBit)
|
|
THEN FailOSErr(permErr);
|
|
|
|
{ make sure this section is not already reading }
|
|
IF SIOCBHandle(sectionH^^.refNum) = NIL THEN
|
|
BEGIN
|
|
{ be sure we have a control block }
|
|
thePubCB := PubCBHandle(sectionH^^.controlBlock);
|
|
IF thePubCB = NIL THEN FailOSErr(fnfErr);
|
|
|
|
{ make sure control block is up to date }
|
|
FailOSErr(dpPubSync(thePubCB));
|
|
|
|
{ create a Section I/O record for this Section }
|
|
FailOSErr(dpCreateSIOCBRecord(sectionH, 0, NIL, sectCB));
|
|
SIOCBHandle(sectionH^^.refNum) := sectCB;
|
|
|
|
{ call EditionOpener }
|
|
openerParams.info := thePubCB^^.info;
|
|
openerParams.sectionH := sectionH;
|
|
FailOSErr(dp_GetCurrentAppRefNum(thisApp));
|
|
FailOSErr(dp_CallEditionOpenerProc(eoOpen, openerParams, thisApp^^.emulator));
|
|
|
|
{ set up fields in I/O control block, now that we have them }
|
|
WITH sectCB^^ DO
|
|
BEGIN
|
|
{sectCB^^.}ioRefNum := openerParams.ioRefNum;
|
|
{sectCB^^.}ioProc := openerParams.ioProc;
|
|
END; {with}
|
|
END ELSE
|
|
BEGIN
|
|
sectCB := SIOCBHandle(sectionH^^.refNum);
|
|
dp_OpenEdition := containerAlreadyOpenWrn;
|
|
END;
|
|
|
|
Success(fi);
|
|
END; { dp_OpenEdition }
|
|
|
|
|
|
|
|
{------------- dp_OpenNewEdition -------------}
|
|
FUNCTION dp_OpenNewEdition(sectionH: SectionHandle; fdCreator: OSType;
|
|
sectionDocument: FSSpecPtr;
|
|
VAR sectCB: SIOCBHandle): OSErr;
|
|
VAR
|
|
thisApp: AppRefNum;
|
|
thePubCB: PubCBHandle;
|
|
openerParams: EditionOpenerParamBlock;
|
|
localSectionDocument: FSSpec;
|
|
openErr: OSErr;
|
|
fi: FailInfo;
|
|
BEGIN
|
|
sectCB := NIL;
|
|
|
|
{ set up failure handling }
|
|
IF isFailure(fi, dp_OpenNewEdition) THEN
|
|
BEGIN
|
|
IF sectCB <> NIL
|
|
THEN DisposHandle(Handle(sectCB));
|
|
sectCB := NIL;
|
|
SIOCBHandle(sectionH^^.refNum) := NIL;
|
|
EXIT(dp_OpenNewEdition);
|
|
END; {if}
|
|
|
|
{ make local copy if not NIL }
|
|
IF sectionDocument <> NIL THEN
|
|
BEGIN
|
|
BlockMove(Ptr(sectionDocument), @localSectionDocument, SizeOf(FSSpec)); { localSectionDocument := sectionDocument^;}
|
|
sectionDocument := @localSectionDocument;
|
|
END;
|
|
|
|
{$IFC qRangeCheck }
|
|
FailOSErr(dpCheckSection(SectionH));
|
|
{$ENDC }
|
|
FailOSErr(dp_GetCurrentAppRefNum(thisApp));
|
|
thePubCB := PubCBHandle(sectionH^^.controlBlock);
|
|
|
|
{ make sure section can write }
|
|
IF NOT bTST(sectionH^^.kind, kCanWriteEditionsBit)
|
|
THEN FailOSErr(wrPermErr);
|
|
|
|
{ make sure it is not already open }
|
|
IF SIOCBHandle(sectionH^^.refNum) <> NIL THEN
|
|
BEGIN
|
|
sectCB := SIOCBHandle(sectionH^^.refNum);
|
|
FailOSErr(containerAlreadyOpenWrn);
|
|
END;
|
|
|
|
{ make sure control block is not NIL } { <38, #c1-MTM-007> }
|
|
IF thePubCB = NIL { <38, #c1-MTM-007> }
|
|
THEN FailOSErr(nsvErr); { <38, #c1-MTM-007> }
|
|
|
|
{ make sure control block is up to date }
|
|
FailOSErr(dpPubSync(thePubCB));
|
|
|
|
{ make sure that the edition file is open }
|
|
WITH thePubCB^^ DO
|
|
BEGIN
|
|
IF IsEditionFile(info.fdType)
|
|
{&} THEN IF (bAND(openMode, dmRequestWritePerm) = 0) THEN
|
|
BEGIN
|
|
{ ahh!! pub file is not open, probably means when the publisher was }
|
|
{ instantiated, there was another publisher on another machine with file open. }
|
|
{ So see if that publisher is gone and 'sectionH' can become the publisher. }
|
|
{ Open the file, to lock it as the publisher }
|
|
openErr := dpPubOpenFile(thePubCB, sectionH^^.kind);
|
|
CASE openErr OF
|
|
permErr, afpDenyConflict, afpAccessDenied:
|
|
FailOSErr(openErr);
|
|
noErr:
|
|
;
|
|
OTHERWISE
|
|
FailOSErr(openErr);
|
|
END; {case}
|
|
END; {if}
|
|
END; {with}
|
|
|
|
{ create a Section I/O record for this Section }
|
|
FailOSErr(dpCreateSIOCBRecord(sectionH, 0, NIL, sectCB));
|
|
SIOCBHandle(sectionH^^.refNum) := sectCB;
|
|
|
|
{ call Edition Opener }
|
|
openerParams.info := thePubCB^^.info;
|
|
openerParams.sectionH := sectionH;
|
|
openerParams.fdCreator := fdCreator;
|
|
openerParams.document := sectionDocument;
|
|
FailOSErr(dp_CallEditionOpenerProc(eoOpenNew, openerParams, thisApp^^.emulator));
|
|
|
|
{ set up fields in I/O control block, now that we have them }
|
|
WITH sectCB^^ DO
|
|
BEGIN
|
|
ioRefNum := openerParams.ioRefNum;
|
|
ioProc := openerParams.ioProc;
|
|
END; {with}
|
|
|
|
Success(fi);
|
|
END; { dp_OpenNewEdition }
|
|
|
|
|
|
|
|
{------------- dp_CloseEdition -------------}
|
|
FUNCTION dp_CloseEdition(sectCB: SIOCBHandle; appWasSuccessful: BOOLEAN): OSErr;
|
|
VAR
|
|
thisApp: AppRefNum;
|
|
sectionH: SectionHandle;
|
|
thePubCB: PubCBHandle;
|
|
params: EditionOpenerParamBlock;
|
|
originalEditionMdDate: TimeStamp;
|
|
fi: FailInfo;
|
|
BEGIN
|
|
DoNotPutInRegister(@sectionH);
|
|
sectionH := NIL;
|
|
|
|
{ set up failure handling }
|
|
IF isFailure(fi, dp_CloseEdition) THEN
|
|
BEGIN
|
|
IF (sectionH <> NIL) {&} THEN IF (SIOCBHandle(sectionH^^.refNum) <> NIL) THEN
|
|
BEGIN
|
|
DisposHandle(Handle(sectCB));
|
|
sectionH^^.refNum := NIL;
|
|
END;
|
|
EXIT(dp_CloseEdition);
|
|
END; {if}
|
|
|
|
FailOSErr(dp_GetCurrentAppRefNum(thisApp));
|
|
|
|
IF sectCB = NIL THEN FailOSErr(rfNumErr);
|
|
|
|
{ get preliminary stuff }
|
|
sectionH := SectionHandle(sectCB^^.section);
|
|
thePubCB := PubCBHandle(sectionH^^.controlBlock);
|
|
|
|
{ make sure it is already opened by this section }
|
|
IF SIOCBHandle(sectionH^^.refNum) <> sectCB THEN FailOSErr(rfNumErr);
|
|
|
|
{ set up call to Edition Opener }
|
|
params.info := thePubCB^^.info;
|
|
params.sectionH := sectionH;
|
|
params.success := appWasSuccessful;
|
|
params.ioRefNum := sectCB^^.ioRefNum;
|
|
params.ioProc := sectCB^^.ioProc;
|
|
|
|
IF thePubCB^^.oldPubCB = NIL THEN
|
|
BEGIN
|
|
{ closing an OpenEdition }
|
|
FailOSErr(dp_CallEditionOpenerProc(eoClose, params, thisApp^^.emulator));
|
|
|
|
{ if successful, then mark that the subscirber has same stuff as edition }
|
|
IF appWasSuccessful
|
|
THEN sectionH^^.mdDate := thePubCB^^.info.mdDate;
|
|
END ELSE
|
|
BEGIN
|
|
{ save off the mdDate of the last successful edition }
|
|
originalEditionMdDate := thePubCB^^.oldPubCB^^.info.mdDate;
|
|
|
|
{ closing an OpenNewEdition }
|
|
FailOSErr(dp_CallEditionOpenerProc(eoCloseNew, params, thisApp^^.emulator));
|
|
|
|
{ control block has changed }
|
|
thePubCB := PubCBHandle(sectionH^^.controlBlock);
|
|
|
|
IF appWasSuccessful THEN
|
|
BEGIN
|
|
{ sync the controlBlock to have the new mod. date }
|
|
thePubCB^^.info.mdDate := sectionH^^.mdDate;
|
|
|
|
{ only send read event to subscribers if data has changed }
|
|
IF originalEditionMdDate <> sectionH^^.mdDate THEN
|
|
BEGIN
|
|
{ send read event to all automatic subscribers }
|
|
FailOSErr(dpNotifySubscribers(thePubCB));
|
|
END;
|
|
|
|
{ now mark this section as 'publisher' for priority in goto publisher }
|
|
FailOSErr(dpMakeItThePublisher(thePubCB, sectionH, thisApp));
|
|
END; {if}
|
|
|
|
END; {else}
|
|
|
|
{ dispose of the Section I/O record for this Section }
|
|
SIOCBHandle(sectionH^^.refNum) := NIL;
|
|
DisposHandle(Handle(sectCB));
|
|
FailOSErr(MemError);
|
|
|
|
Success(fi);
|
|
END; { dp_CloseEdition }
|
|
|
|
|
|
{------------- dp_GetStandardFormats -------------}
|
|
FUNCTION dp_GetStandardFormats(container: EditionContainerSpec; VAR previewFormat: FormatType;
|
|
preview, publisherAlias, formats: Handle): OSErr;
|
|
CONST
|
|
kNumberOfPreviewFormats = 4;
|
|
kNumberOfPriorityArrangements = 3;
|
|
TYPE
|
|
FormatArray = ARRAY [1..kNumberOfPreviewFormats] OF FormatType;
|
|
FormatPriorityList = ARRAY [1..kNumberOfPriorityArrangements] OF FormatArray;
|
|
FormatPriorityListAlt = PACKED ARRAY [1..SizeOf(FormatPriorityList)] OF CHAR;
|
|
VAR
|
|
thisApp: AppRefNum;
|
|
thePubCB: PubCBHandle;
|
|
openerParams: EditionOpenerParamBlock;
|
|
previewErr: OSErr;
|
|
aliasErr: OSErr;
|
|
formatsErr: OSErr;
|
|
i: INTEGER;
|
|
aFormat: FormatType;
|
|
formatsPriority:FormatPriorityList;
|
|
thePriorityList:INTEGER;
|
|
theError: OSErr;
|
|
opened: BOOLEAN;
|
|
fi: FailInfo;
|
|
BEGIN
|
|
DoNotPutInRegister(@thePubCB);
|
|
DoNotPutInRegister(@opened);
|
|
thePubCB := NIL;
|
|
opened := FALSE;
|
|
dp_GetStandardFormats := noErr;
|
|
|
|
{ top level functions must set up a failure handler }
|
|
IF IsFailure(fi, theError) THEN
|
|
BEGIN
|
|
IF opened THEN
|
|
BEGIN
|
|
openerParams.success := FALSE;
|
|
IgnoreOSErr(dp_CallEditionOpenerProc(eoClose, openerParams, thisApp^^.emulator));
|
|
END;
|
|
IF thePubCB <> NIL
|
|
THEN IgnoreOSErr(dpReleasePubCB(thePubCB, thisApp));
|
|
IF theError = eofErr
|
|
THEN dp_GetStandardFormats := noTypeErr { map end-of-file errors into noType errors }
|
|
ELSE dp_GetStandardFormats := theError;
|
|
EXIT(dp_GetStandardFormats);
|
|
END;
|
|
|
|
{ make sure we have a PubCB for it }
|
|
FailOSErr(dp_GetCurrentAppRefNum(thisApp));
|
|
FailOSErr(dpRequestPubCB(container.theFile, thisApp, kCheckExisting, kCanAllocateNew, thePubCB));
|
|
|
|
{ call EditionOpener to open file }
|
|
openerParams.info := thePubCB^^.info;
|
|
openerParams.sectionH := NIL;
|
|
FailOSErr(dp_CallEditionOpenerProc(eoOpen, openerParams, thisApp^^.emulator));
|
|
opened := TRUE;
|
|
|
|
{ look for a preview }
|
|
previewErr := noErr;
|
|
IF preview <> NIL THEN
|
|
BEGIN
|
|
previewErr := noTypeErr;
|
|
|
|
{ set up priority of search }
|
|
FormatPriorityListAlt(formatsPriority) := 'prvwPICTTEXTsnd prvwTEXTPICTsnd prvwsnd TEXTPICT';
|
|
|
|
thePriorityList := 1; {default priority }
|
|
IF LONGINT(openerParams.info.fdType) = LONGINT(kTEXTEditionFileType)
|
|
THEN thePriorityList := 2
|
|
ELSE IF LONGINT(openerParams.info.fdType) = LONGINT(ksndEditionFileType)
|
|
THEN thePriorityList := 3;
|
|
|
|
{ search for format }
|
|
FOR i := 1 TO kNumberOfPreviewFormats DO
|
|
BEGIN
|
|
aFormat := formatsPriority[thePriorityList,i];
|
|
IF dpTryToGetFormat(aFormat, preview, openerParams.ioRefNum, openerParams.ioProc) = noErr THEN
|
|
BEGIN
|
|
previewFormat := aFormat;
|
|
previewErr := noErr;
|
|
LEAVE;
|
|
END; {if}
|
|
END; {for}
|
|
END; {else}
|
|
|
|
{ look for 'alis' }
|
|
aliasErr := noErr;
|
|
IF publisherAlias <> NIL THEN
|
|
BEGIN
|
|
aliasErr := dpTryToGetFormat(kPublisherDocAliasFormat, publisherAlias, openerParams.ioRefNum, openerParams.ioProc);
|
|
END;
|
|
|
|
{ look for 'fmts' }
|
|
formatsErr := noErr;
|
|
IF formats <> NIL THEN
|
|
BEGIN
|
|
formatsErr := dpTryToGetFormat(kFormatListFormat, formats, openerParams.ioRefNum, openerParams.ioProc);
|
|
END;
|
|
|
|
{ call EditionOpener to close file }
|
|
{openerParams.ioRefNum already set }
|
|
{openerParams.ioProc already set }
|
|
{openerParams.sectionH already set }
|
|
openerParams.success := FALSE;
|
|
FailOSErr(dp_CallEditionOpenerProc(eoClose, openerParams, thisApp^^.emulator));
|
|
opened := FALSE;
|
|
|
|
{ release the PubCB }
|
|
FailOSErr(dpReleasePubCB(thePubCB, thisApp));
|
|
thePubCB := NIL;
|
|
|
|
{ return an error code if any of the formats were not found }
|
|
FailOSErr(aliasErr);
|
|
FailOSErr(formatsErr);
|
|
FailOSErr(previewErr);
|
|
|
|
Success(fi);
|
|
END; { dp_GetStandardFormats }
|
|
|