-- CreateCedarVM.Mesa
-- last edited 2-Dec-81  1:09:36 by Paul Rovner

DIRECTORY
    Ascii USING[CR],
    Directory USING[InsertFile, Lookup],
    Exec USING[w],
    File USING[nullCapability, Capability, ShowCapability, Create, Delete, MakePermanent, GetSize, PageCount],
    FileInternal USING[PageGroup, Descriptor],
    FileTypes USING[tUntypedFile],
    LogicalVolume USING[Handle, VolumeAccessProc],
    Runtime USING[CallDebugger],
    TTY USING[PutLine, GetNumber, PutDecimal, PutString, Rubout, GetChar, PutChar],
    VolFileMapImpl USING[GetPageGroup],
    Volume USING[GetAttributes, systemID, ID],
    VolumeImpl USING[VolumeAccess];

CreateCedarVM: PROGRAM
  IMPORTS Directory, Exec, File, TTY, Runtime, VolFileMapImpl, Volume, VolumeImpl
  SHARES Directory, VolFileMapImpl, VolumeImpl
   
= BEGIN

filePages: NAT ← 4096;

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]; 
  };
  
Main: PROC =
{ success: BOOLEAN ← FALSE;

  file: File.Capability ← File.nullCapability;

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

  TTY.PutLine[Exec.w, ""];

  IF Directory.Lookup["CedarVM.DontDeleteMe" ! ANY => CONTINUE] # File.nullCapability
   THEN {TTY.PutLine[Exec.w, "CedarVM.DontDeleteMe already exists. You might want to delete it and try again."];
         RETURN};
 
  TTY.PutString[Exec.w, "Please type desired file size in pages..."];

  filePages ← TTY.GetNumber[h: Exec.w, default: filePages, radix: 10];
  
  IF filePages < 1000 THEN filePages ← 1000 ELSE IF filePages > 16384 THEN filePages ← 16384;
  
  TTY.PutLine[Exec.w, ""];
  TTY.PutString[Exec.w, "Using "];
  TTY.PutDecimal[Exec.w, filePages];
  TTY.PutLine[Exec.w, " for filePages "];
  IF NOT Confirm[] THEN RETURN;
  
  TTY.PutLine[Exec.w, "This takes a while (small number of minutes, maybe). Hang in there..."];

  IF OutOfDisk[filePages]
   THEN {TTY.PutLine[Exec.w, "Failure. Not enough free disk pages."];
	 RETURN};

  file ← File.Create[Volume.systemID, 1000, FileTypes.tUntypedFile];
  TTY.PutChar[Exec.w, '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];
            TTY.PutChar[Exec.w, 'N];
            IF FileIsContiguousOnDisk[file]
	     THEN {success ← TRUE; EXIT}
             ELSE {a[x] ← file;
                   x ← x + 1;
                   IF x >= fileArrayMaxLength OR OutOfDisk[filePages] THEN EXIT;
                   file ← File.Create[Volume.systemID, 1000, FileTypes.tUntypedFile];
		   TTY.PutChar[Exec.w, 'n]}};
      a[x] ← file;
      x ← x + 1;
      IF x >= fileArrayMaxLength OR OutOfDisk[filePages] THEN EXIT;
      file ← File.Create[Volume.systemID, 1000, FileTypes.tUntypedFile];
      TTY.PutChar[Exec.w, 'n];
  ENDLOOP; 
 
  FOR i: NAT IN [0..x)
    DO {TTY.PutChar[Exec.w, IF File.GetSize[a[i]] = 1000 THEN 'd ELSE 'D];
        File.Delete[a[i]]}
   ENDLOOP;

  TTY.PutLine[Exec.w, ""];

  IF success
   THEN {File.MakePermanent[file];
         Directory.InsertFile["CedarVM.DontDeleteMe", file];
         TTY.PutLine[Exec.w,
                     "Success. CedarVM.DontDeleteMe has been created."]}
   ELSE TTY.PutLine[Exec.w,
                    "Failure. The volume is fragmented enough that you'll have to erase it and start from fresh to create the CedarVM file."];
};

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

Confirm: PROC RETURNS[BOOLEAN] =
  { ENABLE TTY.Rubout => GOTO rubout;

    ch: CHARACTER;
    TTY.PutString[Exec.w, "[Confirm] "];
    ch ← UpperCase[TTY.GetChar[Exec.w]];
    SELECT ch FROM
	Ascii.CR, 'Y =>
	    { TTY.PutLine[Exec.w, "(Yes)"];
	      RETURN[TRUE]};
     ENDCASE =>
	    { TTY.PutLine[Exec.w, "(No)"];
	      RETURN[FALSE]};
     EXITS rubout =>
	    { TTY.PutLine[Exec.w, "(No)"];
	      RETURN[FALSE]}};


      -- START HERE

Main[! ANY => {Runtime.CallDebugger["CreateCedarVM failed in some wierd way. Either call Paul Rovner at x4465 or Proceed to forget the whole thing."];
	       CONTINUE}];
  
END.