-- Copyright (C) 1981, 1982, 1984 by Xerox Corporation. All rights reserved. -- NetDirBuilderInterlocks.mesa, HGM, 6-Nov-84 3:32:39 -- From AccessIO of Oct 9, 1980 7:53 PM (Alto version) -- From AccessIO of 15-Apr-81 12:00:24 (Pilot 6.0? version) -- From AccessIO of 12-Jan-82 11:58:35 (Pilot 8.0h version) -- From AccessIO of 23-Dec-82 11:37:12 (Pilot 10.0d version) -- From AccessIO of 13-Aug-84 14:25:10 (Pilot 11.1 version) DIRECTORY Ascii USING [CR], Cursor USING [Object, Fetch, Set, Store], Heap USING [systemZone], Inline USING [LongNumber], FileName USING [ AllocVFN, FreeVFN, PackFilename, ResetVFN, UnpackFilename, VirtualFilename, VirtualFilenameObject], FileTransfer USING [ Connection, Copy, Create, Error, FileInfo, SetPrimaryCredentials, VetoProc], PupLibrarian USING [ AllFromHereID, OpenLibrarianConnection, CreatePropertyList, CredentialsObject, DestroyPropertyList, Error, FileServerObject, FullLibjectID, FullLibjectIDHandle, GetPropertyString, GetPropertyTwoWord, Handle, Activate, Checkin, Checkout, FindID, FindVersion, Query, LibjectID, PropertyList, PropertyPair, SnapShotHandle], PupLibrarianPN USING [ ContentsFile, localnamePN, CheckedInReason, CheckedOutBy, CheckedOutReason, CheckedOutTOD, CheckedInTOD, UserName], MFile USING [Acquire, Error, Handle, Release], MStream USING [Create, Error, Handle], Process USING [Pause, SecondsToTicks, Ticks], Profile USING [GetUser], Put USING [Char, Line, Text], Stream USING [Handle], String USING [AppendString, AppendStringAndGrow, Copy, CopyToNewString, Empty, Equal, Equivalent], Time USING [Append, Current, Packed, Unpack], UserInput USING [UserAbort], PupDefs USING [AppendMyName, PupPackageDestroy, PupPackageMake], NetDirBuilderOps USING [log]; NetDirBuilderInterlocks: PROGRAM IMPORTS Cursor, Heap, PupLibrarian, MFile, MStream, Process, Profile, Put, String, FileName, FileTransfer, Time, UserInput, PupDefs, NetDirBuilderOps EXPORTS NetDirBuilderOps = BEGIN OPEN PupLibrarian, PupLibrarianPN; z: UNCOUNTED ZONE = Heap.systemZone; -- global variable declarations VirtualFilename: TYPE = FileName.VirtualFilename; VirtualFilenameObject: TYPE = FileName.VirtualFilenameObject; ActivateMode: TYPE = {noActivate, activate, activateAndWait}; ErrorLevel: TYPE = {info, warning, error}; -- User and User.cm stuff currentFid: FullLibjectID; person: LONG STRING ← [20]; pwd: LONG STRING ← [20]; connect: LONG STRING ← [40]; connectPwd: LONG STRING ← [20]; primary: PupLibrarian.CredentialsObject ← [person, pwd]; secondary: PupLibrarian.CredentialsObject ← [connect, connectPwd]; reason: LONG STRING ← [40]; errors, warnings: CARDINAL; Switch: TYPE = PACKED ARRAY CHARACTER ['a..'z] OF BOOLEAN; switch: Switch ← ALL[FALSE]; conn: FileTransfer.Connection ← NIL; cmdStream: Stream.Handle ← NIL; --used by GetToken libject: LONG STRING = "<Pup>Pup-Network.txt"; server: PupLibrarian.Handle = PupLibrarian.OpenLibrarianConnection["Marion", pup]; Lock: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN switch['s] ← TRUE; RETURN[PokePupLibrarian[Retrieve]]; END; Abort: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN switch['s] ← FALSE; RETURN[PokePupLibrarian[Store]]; END; Unlock: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN switch['s] ← TRUE; RETURN[PokePupLibrarian[Store]]; END; PokePupLibrarian: PROCEDURE [proc: PROCEDURE [LONG STRING, BOOLEAN]] RETURNS [BOOLEAN] = BEGIN SaveUserInfo: PROCEDURE [name, password: LONG STRING] = BEGIN String.Copy[person, name]; String.Copy[pwd, password]; END; Profile.GetUser[SaveUserInfo, registry]; errors ← warnings ← 0; PupDefs.PupPackageMake[]; reason.length ← 0; PupDefs.AppendMyName[reason]; IF conn = NIL THEN BEGIN conn ← FileTransfer.Create[]; FileTransfer.SetPrimaryCredentials[ conn: conn, user: primary.directory, password: primary.password]; END; proc[ libject, TRUE ! PupLibrarian.Error => {PostMessage[libject, message, error]; CONTINUE}]; PupDefs.PupPackageDestroy[]; RETURN[(errors + warnings) = 0]; END; Aborted: SIGNAL = CODE; Retrieve: PROCEDURE [name: LONG STRING, checkOut: BOOLEAN] = BEGIN fid: FullLibjectIDHandle ← GetCurrentFID[name, activateAndWait]; plist: PropertyList ← GetLibjectInOutInfo[fid, FALSE]; outBy: LONG STRING = GetPropertyString[plist, CheckedOutBy].s; { IF checkOut THEN { IF String.Empty[reason] THEN { PostMessage[name, "no CheckOut reason supplied"L, error]; GOTO alldone}; Checkout[ server, fid, reason ! PupLibrarian.Error => IF code = checkedOut THEN { string: LONG STRING = [80]; outTime: LONG CARDINAL = GetPropertyTwoWord[ plist, CheckedOutTOD].ln.lc; String.AppendString[string, "Already checked out by "L]; String.AppendString[string, outBy]; String.AppendString[string, " at "L]; Time.Append[string, Time.Unpack[[outTime]]]; PostMessage[name, string, error]; GOTO alldone}; UNWIND => GOTO alldone]; PostMessage[name, "Checked Out"L, info]} ELSE {WriteChar[Ascii.CR]}; IF switch['s] THEN RetrieveFiles[name, plist]; GOTO alldone; EXITS alldone => DestroyPropertyList[plist]}; END; BadNameProperty: PROCEDURE [ libject, expandedName: LONG STRING, type: {local, remote}] RETURNS [bad: BOOLEAN] = BEGIN IF (bad ← String.Empty[expandedName]) THEN BEGIN msg: LONG STRING ← z.NEW[StringBody[100]]; -- do not use frame as it is allocated on ALL calls String.AppendString[msg, "has empty "L]; String.AppendString[msg, IF type = local THEN "local"L ELSE "remote"L]; String.AppendString[msg, " name property"L]; PostMessage[libject, msg, warning]; z.FREE[@msg]; END; END; CopyNameProperty: PROCEDURE [plist: PropertyList, type: {local, remote}] RETURNS [name: LONG STRING] = BEGIN name ← String.CopyToNewString[ GetPropertyString[ plist, IF type = local THEN localnamePN ELSE ContentsFile].s, z, 20]; END; RetrieveFiles: PROCEDURE [libject: LONG STRING, plist: PropertyList] = BEGIN remoteName: LONG STRING ← CopyNameProperty[plist, remote]; localName: LONG STRING ← CopyNameProperty[plist, local]; rec: VirtualFilenameObject ← [NIL, NIL, NIL, NIL]; extension: LONG STRING ← [20]; errors: CARDINAL ← 0; IF BadNameProperty[libject, localName, local] THEN RETURN; IF BadNameProperty[libject, remoteName, remote] THEN RETURN; FileName.UnpackFilename[remoteName, @rec]; IF switch['s] THEN { IF String.Empty[rec.version] THEN { PostMessage[remoteName, "Retrieving default version"L, warning]}; RetrieveOneFile[@rec, libject, localName, NIL]}; FileName.ResetVFN[@rec, TRUE, TRUE, TRUE, TRUE]; z.FREE[@localName]; z.FREE[@remoteName]; END; RetrieveOneFile: PROC [rec: VirtualFilename, libject, localName, ext: LONG STRING] = BEGIN targetName: LONG STRING ← NIL; localVFN: VirtualFilename; retrievedVersion: LONG STRING ← [6]; origVersion: LONG STRING = rec.version; checkVersion: BOOLEAN = ~String.Empty[origVersion] AND ~String.Equivalent[origVersion, "h"L]; gotIt: BOOLEAN; RetrieveThisOne: PROC [checkVersion: BOOLEAN] RETURNS [BOOLEAN] = BEGIN localVFN ← FileName.AllocVFN[localName]; SetTargetName[]; WriteString[" Retrieving: "L]; WriteLine[targetName]; FileTransfer.Copy[ sourceFile: rec, destFile: localVFN, sourceConn: conn, veto: IF checkVersion THEN GetVersion ELSE NIL ! FileTransfer.Error => { SELECT TRUE FROM code = skipOperation => REJECT; code = cantModify => { PostMessage[localName, "can't be modified!"L, error]; GOTO fail}; ENDCASE => { PostMessage[localName, "retrieve failure"L, error]; GOTO fail}}]; PostMessage[" Retrieved"L, targetName, info]; RETURN[TRUE]; EXITS fail => RETURN[FALSE] END; GetVersion: FileTransfer.VetoProc = { remoteInfo: FileTransfer.FileInfo = info[conn].source; retrievedVersion.length ← 0; String.AppendString[retrievedVersion, remoteInfo.version]; rec.version ← retrievedVersion; SetTargetName[]; RETURN[do, TRUE]}; SetTargetName: PROCEDURE = { z.FREE[@targetName]; targetName ← FileName.PackFilename[rec, TRUE, TRUE, TRUE, TRUE]}; IF ext # NIL THEN { String.AppendStringAndGrow[@rec.name, ext, z]; String.AppendString[localName, ext]}; rec.version ← NIL; gotIt ← RetrieveThisOne[TRUE]; rec.version ← origVersion; IF gotIt AND checkVersion AND ~String.Equal[origVersion, retrievedVersion] THEN { PostMessage[ libject, "no CheckIn corresponding to most recent store"L, warning]; [] ← RetrieveThisOne[FALSE]}; FileName.FreeVFN[localVFN]; z.FREE[@targetName]; END; Store: PROCEDURE [name: LONG STRING, checkIn: BOOLEAN] = BEGIN dh: MStream.Handle ← NIL; fh: MFile.Handle ← NIL; localName: LONG STRING ← NIL; remoteName: LONG STRING ← NIL; firstStoreError: BOOLEAN ← TRUE; fid: FullLibjectIDHandle ← GetCurrentFID[name, activateAndWait]; plist: PropertyList ← GetLibjectInOutInfo[fid, checkIn]; reasonPP: LONG POINTER TO String PropertyPair; storeSource: BOOLEAN ← switch['s]; DoNotAttemptStores: PROCEDURE = INLINE {storeSource ← FALSE}; localName ← CopyNameProperty[plist, local]; IF storeSource THEN BEGIN IF BadNameProperty[name, localName, local] THEN {DoNotAttemptStores[]} ELSE IF storeSource THEN { fh ← MFile.Acquire[localName, readOnly, [] ! MFile.Error => CONTINUE]; IF fh # NIL THEN { IF checkIn AND storeSource THEN BEGIN dh ← MStream.Create[ fh, [] ! MStream.Error => PostMessage[localName, "cannot be opened!"L, error]]; fh ← NIL; END} ELSE { WriteChar[Ascii.CR]; PostMessage[localName, "NOT on local disk!"L, error]; checkIn ← FALSE; DoNotAttemptStores[]}}; END; IF checkIn THEN BEGIN fsh: PupLibrarian.FileServerObject ← [NIL, primary, secondary]; reasonPP ← GetPropertyString[plist, CheckedInReason].pp; z.FREE[@reasonPP.string]; reasonPP.string ← String.CopyToNewString[reason, z]; remoteName ← Checkin[ server, fid, modification, plist, dh, @fsh ! PupLibrarian.Error => IF code = communicationError OR code = fatalTransferError THEN BEGIN IF firstStoreError THEN PostMessage[localName, "store failure"L, warning]; firstStoreError ← FALSE; IF code = communicationError THEN {WaitForRetry[message, 10]; RESUME }; END; --dh destroyed in PupLibrariansB UNWIND => DestroyPropertyList[plist]]; PostMessage[name, "Checked In"L, info]; IF switch['s] THEN PostMessage[" Stored"L, remoteName, info]; storeSource ← FALSE; END ELSE IF storeSource THEN { outBy: LONG STRING = GetPropertyString[plist, CheckedOutBy].s; IF storeSource AND switch['m] AND ~String.Equivalent[person, outBy] THEN { PostMessage[name, "skipped, since not checked out to you."L, info]; switch['d] ← FALSE; DoNotAttemptStores[]} ELSE {remoteName ← CopyNameProperty[plist, remote]; WriteChar[Ascii.CR]}}; IF storeSource THEN BEGIN IF BadNameProperty[name, remoteName, remote] THEN storeSource ← FALSE ELSE StoreFiles[name, localName, remoteName, ~checkIn AND storeSource]; END; IF fh # NIL THEN BEGIN MFile.Release[fh ! MFile.Error => CONTINUE]; END; DestroyPropertyList[plist]; z.FREE[@remoteName]; z.FREE[@localName]; END; StoreFiles: PROCEDURE [ libject, localName, remoteName: LONG STRING, storeOrig: BOOLEAN] = BEGIN targetFH: MFile.Handle ← NIL; remoteVFN: VirtualFilenameObject ← [NIL, NIL, NIL, NIL]; extension: LONG STRING ← [20]; FileName.UnpackFilename[remoteName, @remoteVFN]; FileName.ResetVFN[@remoteVFN, FALSE, FALSE, FALSE, TRUE]; IF storeOrig THEN StoreOneFile[@remoteVFN, libject, localName, NIL, FALSE, FALSE]; FileName.ResetVFN[@remoteVFN, TRUE, TRUE, TRUE, TRUE]; END; StoreOneFile: PROCEDURE [ rec: VirtualFilename, libject, localName, ext: LONG STRING, missingOK: BOOLEAN, checkFile: BOOLEAN] = BEGIN targetFH: MFile.Handle ← NIL; targetRemoteName: LONG STRING; storedIt: BOOLEAN ← TRUE; doingRetry: BOOLEAN ← FALSE; localVFN: VirtualFilenameObject ← [NIL, NIL, NIL, NIL]; IF ext # NIL THEN BEGIN String.AppendStringAndGrow[@rec.name, ext, z]; String.AppendString[localName, ext]; END; IF checkFile THEN BEGIN -- librarian maps ftp error to something unrecognizable; check if local file exists targetFH ← MFile.Acquire[localName, readOnly, [] ! MFile.Error => CONTINUE]; IF targetFH = NIL THEN { IF ~missingOK THEN PostMessage[localName, "NOT on local disk!"L, warning]; RETURN}; -- don't forget to release handle END; FileName.UnpackFilename[localName, @localVFN]; FileTransfer.Copy[ destFile: rec, sourceFile: @localVFN, destConn: conn ! FileTransfer.Error => { IF code # skipOperation THEN REJECT; PostMessage[localName, "store failure"L, error]; storedIt ← FALSE; CONTINUE}]; IF storedIt THEN { targetRemoteName ← FileName.PackFilename[rec, TRUE, TRUE, TRUE, TRUE]; PostMessage[" Stored"L, targetRemoteName, info]}; z.FREE[@targetRemoteName]; FileName.ResetVFN[@localVFN, TRUE, TRUE, TRUE, TRUE]; END; -- Library Support routines GetCurrentFID: PROCEDURE [name: LONG STRING, mode: ActivateMode] RETURNS [fid: FullLibjectIDHandle] = BEGIN id: LibjectID; active: BOOLEAN; snapFid: FullLibjectID; snapshot: SnapShotHandle ← DESCRIPTOR[@snapFid, 1]; [id, active] ← FindID[ server: server, s: name, activate: mode # noActivate, wait: FALSE]; IF ~active AND mode = activateAndWait THEN { WriteString[" (activating)"L]; Activate[server: server, id: id, wait: TRUE]}; snapFid ← FullLibjectID[ id: AllFromHereID, version: [ type: timeAndDate, body: timeAndDate[ pad: -- Must be default until Service > 5.1! -- , tod: Time.Current[]]]]; currentFid ← FindVersion[server, id, snapshot]↑; fid ← @currentFid; END; GetLibjectInOutInfo: PROCEDURE [ fid: FullLibjectIDHandle, checkInReason: BOOLEAN] RETURNS [plist: PropertyList] = BEGIN plist ← CreatePropertyList[IF checkInReason THEN 6 ELSE 5]; plist[0] ← PropertyPair[FALSE, localnamePN.prefix, String[NIL]]; plist[1] ← PropertyPair[FALSE, ContentsFile.prefix, String[NIL]]; plist[2] ← PropertyPair[FALSE, CheckedOutBy.prefix, String[NIL]]; plist[3] ← PropertyPair[FALSE, CheckedOutTOD.prefix, TwoWord[Inline.LongNumber[lc[0]]]]; plist[4] ← PropertyPair[FALSE, CheckedOutReason.prefix, String[NIL]]; IF checkInReason THEN plist[5] ← PropertyPair[FALSE, CheckedInReason.prefix, String[NIL]]; Query[server, fid, plist]; END; GetLibjectQueryInfo: PROCEDURE [fid: FullLibjectIDHandle] RETURNS [plist: PropertyList] = BEGIN plist ← CreatePropertyList[6]; plist[0] ← PropertyPair[FALSE, CheckedOutBy.prefix, String[NIL]]; plist[1] ← PropertyPair[FALSE, CheckedOutReason.prefix, String[NIL]]; plist[2] ← PropertyPair[FALSE, CheckedInReason.prefix, String[NIL]]; plist[3] ← PropertyPair[FALSE, CheckedInTOD.prefix, TwoWord[Inline.LongNumber[lc[0]]]]; plist[4] ← PropertyPair[FALSE, CheckedOutTOD.prefix, TwoWord[Inline.LongNumber[lc[0]]]]; plist[5] ← PropertyPair[FALSE, UserName.prefix, String[NIL]]; Query[server, fid, plist]; END; WaitForRetry: PROCEDURE [msg: LONG STRING, seconds: CARDINAL] = BEGIN oldCursor: Cursor.Object; ticks: Process.Ticks = Process.SecondsToTicks[1]; Cursor.Fetch[@oldCursor]; Cursor.Set[retry]; WriteString[msg]; WriteString[". Will retry."L]; THROUGH [0..seconds) DO Process.Pause[ticks]; IF UserInput.UserAbort[NetDirBuilderOps.log] THEN BEGIN PostMessage[" Aborted!"L, NIL, warning]; Cursor.Store[@oldCursor]; ERROR Aborted END; ENDLOOP; WriteChar[Ascii.CR]; Cursor.Store[@oldCursor]; END; -- String Support routines PostMessage: PROCEDURE [name, message: LONG STRING, err: ErrorLevel ← info] = BEGIN OPEN String; SELECT err FROM info => NULL; warning => {WriteString["Warning, "L]; warnings ← warnings + 1}; error => {WriteString["Error, "L]; errors ← errors + 1}; ENDCASE; IF name # NIL THEN WriteString[name]; IF message # NIL THEN {WriteString[": "L]; WriteString[message]}; WriteChar[Ascii.CR]; END; -- IODefs still lives WriteChar: PROCEDURE [c: CHARACTER] = BEGIN Put.Char[NetDirBuilderOps.log, c]; END; WriteString: PROCEDURE [s: LONG STRING] = BEGIN Put.Text[NetDirBuilderOps.log, s]; END; WriteLine: PROCEDURE [s: LONG STRING] = BEGIN Put.Line[NetDirBuilderOps.log, s]; END; END.