-- CreateAlpineFileImpl.Mesa
-- last edited November 8, 1983 3:21 pm by Karen Kolling.
-- edited 2-Dec-81 1:09:36 by Paul Rovner.

DIRECTORY
Directory USING[InsertFile, Lookup],
File USING[nullCapability, Capability, ShowCapability, Create, Delete, MakePermanent, GetSize, PageCount],
FileInternal USING[PageGroup, Descriptor],
FileTypes USING[tUntypedFile],
IO USING[card, CR, GetCard, GetChar, PutChar, PutF, PutRope, Signal],
LogicalVolume USING[Handle, VolumeAccessProc],
LongString USING[AppendNumber, AppendString],
Rope USING[ROPE],
UserExec USING[CommandProc, ExecHandle, GetStreams, HistoryEvent, RegisterCommand],
VolFileMapImpl USING[GetPageGroup],
Volume USING[GetAttributes, systemID, ID],
VolumeImpl USING[VolumeAccess];

CreateAlpineFileImpl: PROGRAM
IMPORTS Directory, File, IO, LongString, UserExec, VolFileMapImpl, Volume, VolumeImpl
SHARES Directory, VolFileMapImpl, VolumeImpl

= BEGIN

filePages: NAT ← 4096;

ContigFileSize: CARDINAL = 100;

Bug: SIGNAL = CODE;

OutOfDisk: PROC[pagesNeeded: NAT] RETURNS[BOOLEAN] =
{RETURN[Volume.GetAttributes[Volume.systemID].freePageCount < pagesNeeded]};

FileIsContiguousOnDisk: PROC[file: File.Capability] RETURNS[BOOLEAN] =
{ pageGroup: FileInternal.PageGroup;
contiguous: BOOLEAN ← FALSE;
problem: BOOLEAN ← FALSE;
v: Volume.ID ← Volume.systemID;
fpgs: File.PageCount = File.GetSize[file];

d: FileInternal.Descriptor ← [fileID: File.ShowCapability[file].fID,
volumeID: Volume.systemID,
     body: local[immutable: FALSE,
     temporary: TRUE,
      size: fpgs,
      type: FileTypes.tUntypedFile]];

p: LogicalVolume.VolumeAccessProc =
{[contiguous, pageGroup] ← VolFileMapImpl.GetPageGroup[volume, @d, 0];
problem ← NOT contiguous;
IF problem THEN RETURN[FALSE];
contiguous ← pageGroup.nextFilePage - pageGroup.filePage = fpgs;
RETURN[FALSE]};

IF VolumeImpl.VolumeAccess[@v, p, FALSE] # ok OR problem
THEN ERROR Bug
ELSE RETURN[contiguous];
};

--CommandProc: TYPE = PROC [event: HistoryEvent, exec: ExecHandle, clientData: REF ANYNIL] RETURNS[ok: BOOLEANTRUE, msg: ROPENIL];
Main: SAFE PROC [event: UserExec.HistoryEvent, exec: UserExec.ExecHandle, clientData: REF ANY] RETURNS[ok: BOOLEAN, msg: Rope.ROPE] = TRUSTED
{ success: BOOLEAN ← FALSE;

file: File.Capability ← File.nullCapability;
fileName: LONG STRING ← [25];
fileNumber: CARDINAL;

fileArrayMaxLength: NAT = 40;
a: ARRAY [0..fileArrayMaxLength) OF File.Capability ← ALL[File.nullCapability];
x: NAT ← 0;

UserExec.GetStreams[exec].out.PutRope["
Please type desired file index [0..2000]..."];
fileNumber ← UserExec.GetStreams[exec].in.GetCard[];
IF fileNumber > 2000 THEN ERROR;

fileName.length ← 0;
LongString.AppendString[fileName, "Alpine"];
LongString.AppendNumber[fileName, fileNumber, 10];
LongString.AppendString[fileName, ".DontDeleteMe"];

IF Directory.Lookup[fileName ! ANY => CONTINUE] # File.nullCapability
THEN {UserExec.GetStreams[exec].out.PutRope["file already exists. You might want to delete it and try again.
"];
RETURN};

UserExec.GetStreams[exec].out.PutRope["
Please type desired file size in pages..."];

filePages ← UserExec.GetStreams[exec].in.GetCard[];

IF filePages < 1 THEN filePages ← 1 ELSE IF filePages > 16384 THEN filePages ← 16384;

UserExec.GetStreams[exec].out.PutF["*nUsing %g for filePages *n", IO.card[filePages]];
IF NOT Confirm[exec] THEN RETURN;

UserExec.GetStreams[exec].out.PutRope["This takes a while (small number of minutes, maybe). Hang in there..."];

IF OutOfDisk[ContigFileSize]
THEN {UserExec.GetStreams[exec].out.PutRope["Failure. Not enough free disk pages.
"];
  RETURN};

file ← File.Create[Volume.systemID, ContigFileSize, FileTypes.tUntypedFile];
UserExec.GetStreams[exec].out.PutChar['n];

DO IF FileIsContiguousOnDisk[file]
THEN {a[x] ← file;
x ← x + 1;
IF x >= fileArrayMaxLength OR OutOfDisk[filePages] THEN EXIT;
file ← File.Create[Volume.systemID, filePages, FileTypes.tUntypedFile];
UserExec.GetStreams[exec].out.PutChar['N];
IF FileIsContiguousOnDisk[file]
  THEN {success ← TRUE; EXIT}
ELSE {a[x] ← file;
x ← x + 1;
IF x >= fileArrayMaxLength OR OutOfDisk[ContigFileSize] THEN EXIT;
file ← File.Create[Volume.systemID, ContigFileSize, FileTypes.tUntypedFile];
   UserExec.GetStreams[exec].out.PutChar['n]}};
a[x] ← file;
x ← x + 1;
IF x >= fileArrayMaxLength OR OutOfDisk[ContigFileSize] THEN EXIT;
file ← File.Create[Volume.systemID, ContigFileSize, FileTypes.tUntypedFile];
UserExec.GetStreams[exec].out.PutChar['n];
ENDLOOP;

FOR i: NAT IN [0..x)
DO {UserExec.GetStreams[exec].out.PutChar[IF File.GetSize[a[i]] = ContigFileSize THEN 'd ELSE 'D];
File.Delete[a[i]]}
ENDLOOP;

UserExec.GetStreams[exec].out.PutRope["
"];

IF success
THEN {File.MakePermanent[file];
Directory.InsertFile[fileName, file];
UserExec.GetStreams[exec].out.PutRope["Success. file has been created.
"];
}
ELSE UserExec.GetStreams[exec].out.PutRope[
"Failure. The volume is fragmented enough that you'll have to erase it and start from fresh to create the Alpine file.
"];
};

UpperCase: PROC[ch: CHARACTER] RETURNS[CHARACTER] =
{SELECT ch FROM
IN ['a..'z] => RETURN[ch - 'a + 'A];
ENDCASE => RETURN[ch]};

Confirm: PROC[exec: UserExec.ExecHandle] RETURNS[BOOLEAN] =
{ ENABLE IO.Signal => TRUSTED BEGIN IF ec = Rubout THEN GOTO rubout; END;

ch: CHARACTER;
UserExec.GetStreams[exec].out.PutRope["[Confirm] "];
ch ← UpperCase[UserExec.GetStreams[exec].in.GetChar[]];
SELECT ch FROM
 IO.CR, 'Y =>
  { UserExec.GetStreams[exec].out.PutRope["(Yes)
  "];
  RETURN[TRUE]};
ENDCASE =>
  { UserExec.GetStreams[exec].out.PutRope["(No)
  "];
  RETURN[FALSE]};
EXITS rubout =>
  { UserExec.GetStreams[exec].out.PutRope["(No)
  "];
  RETURN[FALSE]}};


-- START HERE

UserExec.RegisterCommand[name: "Main", proc: Main];

END.