-- Copyright (C) 1984 by Xerox Corporation. All rights reserved.
-- OthelloFetchImpl.mesa
-- Johnsson 26-Feb-84 15:24:23
-- Davirro 5-Nov-84 10:53:21
-- Masinter 16-Dec-84 15:54:49
DIRECTORY
Auth,
Environment USING [bytesPerPage, bytesPerWord],
File USING [
Create, Delete, File, MakePermanent, nullFile, PageNumber, PageCount, GetSize, SetSize, Unknown],
FileTypes USING [tUntypedFile],
Heap USING [systemZone],
NSName,
NSString,
OthelloDefs USING [
AbortingCommand, CommandProcessor, Confirm, FlipCursor, GetLvIDFromUser, GetName,
IndexTooLarge, LeaderPage, leaderPages, lpNoteLength, lpVersion,
MyNameIs, Question, RegisterCommandProc, WriteLine, WriteLongNumber, WriteString, Yes],
OthelloFetch USING [Destination, Handle, Object],
OthelloOps USING [
BootFileType, GetVolumeBootFile, GetPhysicalVolumeBootFile, MakeBootable, MakeUnbootable,
SetPhysicalVolumeBootFile, SetVolumeBootFile, VoidVolumeBootFile, VoidPhysicalVolumeBootFile],
PhysicalVolume USING [ID],
Process USING [Detach],
Profile ,
Space USING [Map, CopyIn, ScratchMap, Unmap, Window],
Stream,
String ,
TemporaryBooting USING [InvalidParameters],
Volume USING [Close, GetAttributes, ID, InsufficientSpace, nullID, Open];
OthelloFetchImpl: PROGRAM
IMPORTS Auth, File, Heap, NSString, OthelloDefs, OthelloOps, Process, Space, Stream, String, TemporaryBooting, Volume
EXPORTS OthelloDefs, OthelloFetch, Profile =
BEGIN
Object: TYPE = OthelloFetch.Object;
Handle: TYPE = OthelloFetch.Handle;
list: Handle ← NIL;
current: Handle ← NIL;
cmFile: LONG STRING ← NIL;
fileName: LONG STRING ← NIL;
z: UNCOUNTED ZONE = Heap.systemZone;
S: PROC [s: LONG STRING] RETURNS [NSString.String] = INLINE {
RETURN[NSString.StringFromMesaString[s]]};
-- Fetcher registration
Register: PUBLIC PROC [h: Handle] =
BEGIN
h.next ← list;
list← h;
END;
Select: PUBLIC PROC [h: Handle] =
BEGIN
IF current # NIL THEN current.Close[];
current← h;
END;
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- String/Credentials Commands
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
domain, org: LONG STRING ← NIL;
GetDefaultDomain: --Profile--PUBLIC PROC [proc: PROC [LONG STRING]] = {
proc[domain]};
GetDefaultOrganization: --Profile--PUBLIC PROC [proc: PROC [LONG STRING]] = {
proc[org]};
GetID: --Profile--PUBLIC PROC [
flavor: Auth.Flavor, proc: PROC [id: Auth.IdentityHandle]] =
BEGIN
SELECT flavor FROM
simple => proc[simple];
strong => proc[strong];
ENDCASE => ERROR;
END;
Qualify: --Profile--PUBLIC PROC [
token, newToken: LONG STRING,
qualification: Profile.Qualification ← clearinghouse] = {
namePart: CARDINAL ← String.Length[token];
currentQual: Profile.Qualification ← none;
octalAddress: BOOLEAN ← TRUE; -- only '0..'7 and '# allowed
ss: String.SubStringDescriptor;
chChar: CHARACTER = ':;
regChar: CHARACTER = '.;
IF String.Length[token] = 0 THEN RETURN;
FOR i: CARDINAL IN [0..token.length) DO
SELECT token[i] FROM
regChar => {namePart ← i; currentQual ← registry}; -- look for last dot
chChar => {namePart ← i; currentQual ← clearinghouse; EXIT}; -- first :
IN['0..'7], '# => NULL;
ENDCASE => octalAddress ← FALSE;
ENDLOOP;
IF currentQual = qualification OR octalAddress THEN {
String.AppendString[newToken, token]; RETURN};
ss ← [base: token, offset: 0, length: namePart];
String.AppendSubString[newToken, @ss];
SELECT qualification FROM
none => NULL;
<<registry =>
IF String.Length[defaultRegistry] > 0 THEN {
String.AppendChar[newToken, regChar];
String.AppendString[newToken, defaultRegistry]};>>
clearinghouse =>
IF String.Length[domain] > 0 OR
String.Length[org] > 0 THEN {
String.AppendChar[newToken, chChar];
String.AppendString[newToken, domain];
String.AppendChar[newToken, chChar];
String.AppendString[newToken, org]};
ENDCASE};
ClearinghouseCmd: PROC = {
OthelloDefs.MyNameIs[
myNameIs: "Clearinghouse"L,
myHelpIs: "Set defaults for Clearinghouse"L];
OthelloDefs.GetName["Domain: "L, @domain];
OthelloDefs.GetName["Organization: "L, @org]};
userName: PUBLIC LONG STRING ← NIL;
userPassword: PUBLIC LONG STRING ← NIL;
strong, simple: Auth.IdentityHandle ← NIL;
LoginCmd: PROC = {
OthelloDefs.MyNameIs[
myNameIs: "Login"L, myHelpIs: "Set user name & password"L];
OthelloDefs.GetName["User: "L, @userName];
OthelloDefs.GetName["Password: "L, @userPassword, stars];
IF simple # NIL THEN Auth.FreeIdentity[@simple, z];
IF strong # NIL THEN Auth.FreeIdentity[@strong, z];
simple ← MakeID[flavor: simple];
strong ← MakeID[flavor: strong]};
MakeID: PROC [flavor: Auth.Flavor] RETURNS [id: Auth.IdentityHandle] = {
myName: NSName.NameRecord ← [
org: S[org], domain: S[domain], local: S[userName]];
id ← Auth.MakeIdentity[
myName: @myName,
password: S[userPassword],
z: z,
style: flavor,
dontCheck: TRUE]};
directory: PUBLIC LONG STRING ← NIL;
Directory: PROC = {
OthelloDefs.MyNameIs[
myNameIs: "Directory"L,
myHelpIs: "Set Default FTP directory"L];
OthelloDefs.GetName["Directory: "L, @directory]};
-- Simple Fetches
FetchBoot: PROC = {
Fetch[pilot, "Boot file name: "L, "Fetch Boot File"L, "Fetch Boot File"L, "boot"L]};
FetchGerm: PROC = {
Fetch[germ, "Germ file name: "L, "Germ Fetch"L, "Fetch Germ"L, "germ"L]};
FetchPilotMicrocode: PROC = {
Fetch[
softMicrocode,
"Pilot microcode file name: "L,
"Pilot Microcode Fetch"L,
"Fetch and Install Pilot Microcode"L,
"db"L]};
FetchDiagnosticMicrocode: PROC = {
Fetch[
hardMicrocode,
"Diagnostic microcode file name: "L,
"Diagnostic Microcode Fetch"L,
"Fetch and Install Diagnostic Microcode"L,
"db"L]};
FetchLisp: PROC = {
Fetch[
hardMicrocode,
"Lisp sysout file name: "L,
"Lisp Sysout Fetch"L,
"Fetch and Install Interlisp Sysout"L,
"sysout"L]};
Fetch: PROC [
type: OthelloOps.BootFileType, prompt, name, helpMsg, extension: STRING] = {
created: BOOLEAN;
file: File.File;
firstPage: File.PageNumber;
lvID: Volume.ID;
OthelloDefs.MyNameIs[myNameIs: name, myHelpIs: helpMsg];
CheckOpen[];
lvID ← OthelloDefs.GetLvIDFromUser[].lvID;
OthelloDefs.GetName[prompt, @fileName];
Volume.Open[lvID];
[file, firstPage] ← OthelloOps.GetVolumeBootFile[lvID, type];
IF (created ← file = File.nullFile) THEN
file ← File.Create[lvID, 1, FileTypes.tUntypedFile]
ELSE OthelloOps.MakeUnbootable[file, type, firstPage !
File.Unknown => CONTINUE;
TemporaryBooting.InvalidParameters => {
OthelloDefs.WriteLine["Warning, trouble making unbootable"L];
CONTINUE}];
current.Retrieve[fileName, [pilotFileSystemWrite[file]]
! UNWIND => {IF created THEN File.Delete[file]; Volume.Close[lvID]}];
OthelloDefs.WriteString["Installing..."L];
OthelloOps.SetVolumeBootFile[file, type, OthelloDefs.leaderPages];
IF created THEN File.MakePermanent[file];
-- add for LISP
IF String.Equal[extension, "sysout"L] THEN {
OthelloDefs.WriteString["Expanding to fill volume..."L];
SetBootFileSize[file,lvID];
OthelloDefs.WriteString["OK..."L];
};
-- end
OthelloOps.MakeBootable[file, type, OthelloDefs.leaderPages
! TemporaryBooting.InvalidParameters => {
OthelloDefs.WriteLine["Warning, trouble making bootable"L]; CONTINUE}];
OthelloDefs.WriteLine["done"L];
IF type IN [hardMicrocode..germ] AND
OthelloDefs.Yes["Shall I also use this for the Physical Volume? "L
! UNWIND => Volume.Close[lvID]] THEN
OthelloOps.SetPhysicalVolumeBootFile[file, type, OthelloDefs.leaderPages];
Volume.Close[lvID]};
bufPages: CARDINAL = 8;
StartFeedback: PUBLIC SIGNAL = CODE;
GrabBitsFromStream: PUBLIC PROC [
rs: Stream.Handle, rsSizePages: LONG CARDINAL,
destination: OthelloFetch.Destination, note: LONG STRING ← NIL] = {
WITH destination SELECT FROM
pilotFileSystemWrite => {
buffer: LONG POINTER ← NIL;
base: File.PageNumber ← 0;
got: CARDINAL;
File.SetSize[localFile, rsSizePages + OthelloDefs.leaderPages
! Volume.InsufficientSpace => OthelloDefs.AbortingCommand["Volume Full"L]];
SetLeaderPage[localFile, note];
SIGNAL StartFeedback;
WHILE base < rsSizePages DO
thisPages: CARDINAL = CARDINAL[MIN[rsSizePages-base, bufPages]];
size: CARDINAL = thisPages*Environment.bytesPerPage;
start: CARDINAL ← 0;
buffer ← Space.Map[
window:[localFile, base+OthelloDefs.leaderPages, thisPages],
life: dead].pointer;
DO
[bytesTransferred: got] ← rs.GetBlock[[
blockPointer: buffer, startIndex: start, stopIndexPlusOne: size] !
Stream.EndOfStream => {
got ← 0; start ← start + nextIndex; CONTINUE};
UNWIND => [] ← Space.Unmap[buffer]];
IF got = 0 THEN {[] ← Space.Unmap[buffer]; RETURN};
IF (start ← start + got) = size THEN EXIT;
ENDLOOP;
--buffer ← Space.Unmap[buffer, return];
Process.Detach[LOOPHOLE[FORK Space.Unmap[buffer]]]; buffer ← NIL;
OthelloDefs.FlipCursor[];
base ← base + thisPages;
ENDLOOP;
buffer ← Space.ScratchMap[1]; -- check for any leftover stuff
[bytesTransferred: got] ← rs.GetBlock[[
blockPointer: buffer, startIndex: 0,
stopIndexPlusOne: Environment.bytesPerPage] !
Stream.EndOfStream => {got ← nextIndex; CONTINUE};
UNWIND => [] ← Space.Unmap[buffer]];
[] ← Space.Unmap[buffer];
IF got # 0 THEN OthelloDefs.AbortingCommand[
"File longer than advertised length"L]};
string => {
SIGNAL StartFeedback;
DO
stringOverhead: CARDINAL = SIZE[StringBody]*Environment.bytesPerWord;
string: LONG STRING = Space.ScratchMap[bufPages];
string↑ ← [
length: 0,
maxlength: bufPages*Environment.bytesPerPage - stringOverhead,
text: ];
WHILE string.length < string.maxlength DO
got: CARDINAL;
[bytesTransferred: got] ← rs.get[
rs,
[blockPointer: LOOPHOLE[@string.text],
startIndex: string.length, stopIndexPlusOne: string.maxlength],
rs.options
! Stream.EndOfStream => {
got ← 0; string.length ← string.length + nextIndex; CONTINUE};
UNWIND => [] ← Space.Unmap[string]];
IF got = 0 THEN {
stringProc[string! UNWIND => [] ← Space.Unmap[string]];
[] ← Space.Unmap[string]; RETURN};
string.length ← string.length + got;
ENDLOOP;
[] ← Space.Unmap[string];
OthelloDefs.AbortingCommand["Command file too long!"L];
ENDLOOP};
rawWrite =>{
buffer: LONG POINTER = Space.ScratchMap[1];
done: BOOLEAN ← FALSE;
first: BOOLEAN ← TRUE;
options: Stream.InputOptions ← rs.options;
GetPage: PROC RETURNS [LONG POINTER] = {
got: CARDINAL; index: CARDINAL ← 0;
IF first THEN {SIGNAL StartFeedback; first ← FALSE};
WHILE ~done DO
[bytesTransferred: got] ← rs.get[
sH: rs,
block: [blockPointer: buffer, startIndex: index,
stopIndexPlusOne: Environment.bytesPerPage],
options: options
! Stream.EndOfStream => {got ← nextIndex; done ← TRUE; CONTINUE}];
IF (index ← index + got) = Environment.bytesPerPage
OR done THEN {OthelloDefs.FlipCursor[]; EXIT}
ENDLOOP;
RETURN[IF done AND index = 0 THEN NIL ELSE buffer]};
options.signalEndOfStream ← TRUE;
linkProc[GetPage ! UNWIND => [] ← Space.Unmap[buffer]];
WHILE ~done DO [] ← GetPage[! UNWIND => [] ← Space.Unmap[buffer]] ENDLOOP;
[] ← Space.Unmap[buffer]};
ENDCASE => ERROR};
-- Initial Ucode Fetch Command
FetchInitialMicrocode: PUBLIC PROC [
InstallProc: PROC [getPage: PROC RETURNS [LONG POINTER]]] = {
CheckOpen[];
OthelloDefs.GetName["File name: "L, @fileName];
OthelloDefs.Confirm[];
current.Retrieve[fileName, [rawWrite[InstallProc]]]};
-- Command Files
AlternateGetCMFile: PUBLIC PROC [s: STRING] = {
z.FREE[@cmFile];
cmFile ← z.NEW[StringBody[s.length+8]];
FOR i: CARDINAL IN [1..s.length) DO
String.AppendChar[cmFile, s[i]] ENDLOOP;
DoIndirect[]};
Indirect: PROC = {
OthelloDefs.MyNameIs[
myNameIs: "@", myHelpIs: "Run command file"L];
OthelloDefs.GetName["Command file: "L, @cmFile
! OthelloDefs.Question => {
OthelloDefs.WriteLine["[Host]<Dir>Filename"L]; RESUME}];
DoIndirect[]};
DoIndirect: PROC = {
[] ← String.AppendExtensionIfNeeded[@cmFile, "othello"L, z];
FOR h: Handle ← list, h.next UNTIL h = NIL DO
IF h.DoIndirect[cmFile] THEN RETURN;
ENDLOOP;
OthelloDefs.AbortingCommand["Unrecognizable command file name"L]};
-- Misc. commands
CloseCmd: PROC = {
OthelloDefs.MyNameIs[
myNameIs: "Close"L, myHelpIs: "Close currently open connection"L];
CloseFetch[]};
CloseFetch: PUBLIC PROC = {
Select[NIL]};
ListCmd: PROC = {
OthelloDefs.MyNameIs[
myNameIs: "List Files"L, myHelpIs: "Enumerate files matching pattern"L];
CheckOpen[];
OthelloDefs.GetName["Pattern: "L, @fileName
! OthelloDefs.Question => {
OthelloDefs.WriteLine["pattern to match"L]; RESUME}];
current.List[fileName]};
CheckOpen: PROC = {
IF current = NIL THEN
OthelloDefs.AbortingCommand["You must execute an Open command first"L]};
SetLeaderPage: PUBLIC PROCEDURE [file: File.File, note: LONG STRING] =
BEGIN
lp: LONG POINTER TO OthelloDefs.LeaderPage ← Space.Map[[file, 0, OthelloDefs.leaderPages]].pointer;
lp.version ← OthelloDefs.lpVersion;
lp.length ← MIN[note.length, OthelloDefs.lpNoteLength];
FOR i: CARDINAL IN [0..lp.length) DO
lp.note[i] ← note[i];
ENDLOOP;
[] ← Space.Unmap[lp];
END;
-- Commands for Interlisp-D
CopyLisp: PROC = BEGIN
fromVolume, toVolume: Volume.ID;
fromFile, toFile: File.File;
pvID: PhysicalVolume.ID;
error: BOOLEAN ← FALSE;
currentBase: File.PageNumber;
nextCount: File.PageCount;
fromWindow,toWindow: Space.Window;
toPointer: LONG POINTER;
fromPage1: LONG POINTER TO ARRAY[0..20] OF WORD; -- Lisp Interface Page
fromFileSize: File.PageCount;
fromFileActive: CARDINAL;
tries: CARDINAL;
toFileSize: File.PageCount;
minFileSize: File.PageCount;
OthelloDefs.MyNameIs[
myNameIs: "Copy Lisp From Another Volume"L,
myHelpIs: "Copies lisp from one volume to another"L];
[pvID, fromVolume] ← OthelloDefs.GetLvIDFromUser[prompt:"Volume to copy from: "L];
IF fromVolume = Volume.nullID THEN OthelloDefs.AbortingCommand["Invalid volume."L];
toVolume ← OthelloDefs.GetLvIDFromUser[prompt:"Volume to copy to: "L].lvID;
IF toVolume = Volume.nullID THEN OthelloDefs.AbortingCommand["Invalid volume."L];
-- Open up the volumes
Volume.Open[fromVolume];
Volume.Open[toVolume];
-- Is there a source file?
fromFile ← OthelloOps.GetVolumeBootFile[fromVolume,hardMicrocode].file;
IF fromFile = File.nullFile THEN {
Volume.Close[fromVolume];
Volume.Close[toVolume];
OthelloDefs.AbortingCommand["No Lisp sysout on source volume"L];
};
-- Is there a dest file? Delete it if yes.
toFile ← OthelloOps.GetVolumeBootFile[toVolume,hardMicrocode].file;
IF toFile # File.nullFile THEN {
OthelloOps.VoidVolumeBootFile[toVolume,hardMicrocode];
File.Delete[toFile];
IF OthelloOps.GetPhysicalVolumeBootFile[pvID,hardMicrocode].file = toFile
THEN OthelloOps.VoidPhysicalVolumeBootFile[pvID,hardMicrocode];
};
-- Create the dest file
fromWindow ← [file: fromFile,
base: 1 + OthelloDefs.leaderPages,
count: 1];
fromPage1 ← Space.Map[fromWindow].pointer;
fromFileSize ← File.GetSize[fromFile];
fromFileActive ← fromPage1[20];
fromPage1 ← Space.Unmap[fromPage1];
IF fromFileActive < 1000 THEN {Volume.Close[fromVolume];
Volume.Close[toVolume];
OthelloDefs.AbortingCommand["Source volume doesn't contain a Lisp system!"L];};
IF fromFileActive > fromFileSize THEN {Volume.Close[fromVolume];
Volume.Close[toVolume];
OthelloDefs.AbortingCommand["Can't copy a VMEM that has been already expanded by Lisp!"L];};
toFileSize ← fromFileActive;
tries ← 1;
toFile ← File.Create[toVolume, toFileSize, FileTypes.tUntypedFile
! Volume.InsufficientSpace =>
{ tries ← tries - 1;
toFileSize ← toFileSize - 100;
OthelloDefs.WriteString["."L];
IF tries = 0 OR toFileSize < 0 THEN {error ← TRUE;
CONTINUE}
ELSE RETRY;};];
IF error THEN {Volume.Close[fromVolume];
Volume.Close[toVolume];
OthelloDefs.AbortingCommand["Destination volume does not contain sufficient space!"L];};
File.MakePermanent[toFile];
-- Create windows onto the file
OthelloDefs.WriteString["Copying "L];
OthelloDefs.WriteLongNumber[toFileSize];
OthelloDefs.WriteString["pages... "L];
currentBase ← 0;
nextCount ← 100;
DO -- until file has been copied
nextCount ← MIN[100,fromFileActive-currentBase];
IF nextCount=0 THEN EXIT;
fromWindow ← [file: fromFile,
base: currentBase,
count: nextCount];
toWindow ← [file: toFile,
base: currentBase,
count: nextCount];
toPointer ← Space.Map[toWindow].pointer;
nextCount ← Space.CopyIn[toPointer,fromWindow];
toPointer ← Space.Unmap[toPointer];
currentBase ← currentBase + nextCount;
OthelloDefs.WriteString["."L];
IF currentBase >= minFileSize THEN EXIT;
ENDLOOP;
OthelloDefs.WriteString["Installing..."L];
OthelloOps.MakeBootable[toFile, hardMicrocode, OthelloDefs.leaderPages
! TemporaryBooting.InvalidParameters => {
OthelloDefs.WriteLine["Warning, trouble making bootable"L]; CONTINUE}];
OthelloOps.SetVolumeBootFile[toFile,hardMicrocode,OthelloDefs.leaderPages];
OthelloDefs.WriteLine["done"L];
Volume.Close[fromVolume];
Volume.Close[toVolume];
END;
-- This kludge sets the file's size to be as big as Pilot will let it be, on 100 page increments.
SetBootFileSize: PRIVATE PROC[file: File.File, lvID: Volume.ID] = BEGIN
tries: CARDINAL;
oldSize: File.PageCount ← File.GetSize[file];
newSize: File.PageCount ← oldSize + Volume.GetAttributes[lvID].freePageCount;
tries ← 20;
OthelloOps.MakeUnbootable[file, hardMicrocode, OthelloDefs.leaderPages];
File.SetSize[file, newSize
! File.Unknown => {OthelloDefs.WriteLine["Warning: Trouble making file fill volume - File.Unknown"L];
CONTINUE};
Volume.InsufficientSpace => {OthelloDefs.WriteString["."L]; newSize ← newSize - 100;
tries ← tries - 1;
IF tries = 0 OR newSize < oldSize THEN {OthelloDefs.WriteLine["Warning: Expanding volume failed!"]; CONTINUE;} ELSE RETRY};];
OthelloOps.MakeBootable[file,hardMicrocode, OthelloDefs.leaderPages];
END;
ExpandLisp: PROC = BEGIN
pvID: PhysicalVolume.ID;
exVolume: Volume.ID;
exFile: File.File;
OthelloDefs.MyNameIs[
myNameIs: "Expand Lisp Virtual Memory File"L,
myHelpIs: "Expand the lisp virtual memory file on a volume"L];
[pvID, exVolume] ← OthelloDefs.GetLvIDFromUser[prompt:"Volume to expand: "L];
IF exVolume = Volume.nullID THEN OthelloDefs.AbortingCommand["Invalid volume."L];
Volume.Open[exVolume];
exFile ← OthelloOps.GetVolumeBootFile[exVolume, hardMicrocode].file;
IF exFile = File.nullFile THEN OthelloDefs.AbortingCommand["No sysout on volume."L];
OthelloDefs.WriteString["Expanding file..."L];
SetBootFileSize[exFile,exVolume];
OthelloDefs.WriteLine["Done."L];
Volume.Close[exVolume];
END;
-- command processor
commandProcessor: OthelloDefs.CommandProcessor ← [FetchCommands];
FetchCommands: PROC [index: CARDINAL] = {
SELECT index FROM
0 => Indirect[];
1 => ClearinghouseCmd[];
2 => CloseCmd[];
3 => Directory[];
4 => FetchBoot[];
5 => FetchDiagnosticMicrocode[];
6 => FetchGerm[];
7 => FetchPilotMicrocode[];
8 => ListCmd[];
9 => LoginCmd[];
10 => FetchLisp[];
11 => CopyLisp[];
12 => ExpandLisp[];
ENDCASE => OthelloDefs.IndexTooLarge};
-- init
OthelloDefs.RegisterCommandProc[@commandProcessor];
END.