-- file OSMiscOpsImpl.Mesa
-- last modified by Satterthwaite, August 26, 1982 3:42 pm

DIRECTORY
  DCSFileTypes: TYPE USING [tLeaderPage],
  Directory: TYPE USING [
    CreateFile, DeleteFile, Error, Lookup, RemoveFile, Rename, UpdateDates,
    defaultContext, fileMaxPermissions, ignore],
  File: TYPE USING [Capability, nullCapability, Permissions, read],
  Inline: TYPE USING [LongNumber, BITXOR],
  KernelFile: TYPE USING [MakeTemporary],
  OSMiscOps: TYPE USING [FileAccess, Permissions],
  ProcessorFace: TYPE USING [processorID],
  Runtime: TYPE USING [GetBcdTime, GetTableBase],
  Space: TYPE USING [
    Create, Delete, GetHandle, Handle, LongPointer,
    Map, PageFromLongPointer, virtualMemory, wordsPerPage],
  Time: TYPE USING [Current],
  TimeStamp: TYPE USING [Stamp];

OSMiscOpsImpl: PROGRAM
    IMPORTS Directory, Inline, KernelFile, ProcessorFace, Runtime, Space, Time
    EXPORTS OSMiscOps 
    SHARES ProcessorFace = {

  PageSize: CARDINAL = Space.wordsPerPage;


  -- bulk free storage management
 
  Pages: PUBLIC PROC [n: CARDINAL] RETURNS [base: LONG POINTER] = {
    IF n = 0 THEN base ← NIL
    ELSE {
      space: Space.Handle = Space.Create[size: n, parent: Space.virtualMemory];
      space.Map[];
      base ← space.LongPointer};
    RETURN};

  FreePages: PUBLIC PROC [base: LONG POINTER] = {
    IF base # NIL THEN
      Space.Delete[Space.GetHandle[Space.PageFromLongPointer[base]]]};
    
   
 -- binary table management
 
  GetTableBase: PUBLIC PROC [p: PROGRAM] RETURNS [LONG POINTER] = {
    RETURN [Runtime.GetTableBase[p]]};


 -- version stamp management

  GetNetAndHost: PROC RETURNS [net, host: CARDINAL] = { 
    sum: WORD = Inline.BITXOR[
        ProcessorFace.processorID.a,
        Inline.BITXOR[ProcessorFace.processorID.b, ProcessorFace.processorID.c]];
    net ← sum/256;  host ← sum MOD 256};


  lastTime: LONG CARDINAL ← 0;

  GenerateUniqueId: PUBLIC PROC RETURNS [TimeStamp.Stamp] = {
    net, host: CARDINAL;
    time: LONG CARDINAL;
    [net, host] ← GetNetAndHost[];
    DO
      time ← Time.Current[];
      IF lastTime = 0 OR time # lastTime THEN EXIT;
      ENDLOOP;
    lastTime ← time;
    RETURN [TimeStamp.Stamp[net: net, host: host, time: time]]};


  BcdCreateTime: PUBLIC PROC RETURNS [time: LONG CARDINAL] = {
    RETURN [Runtime.GetBcdTime[]]};

  ImageId: PUBLIC PROC RETURNS [TimeStamp.Stamp] = {
    RETURN [TimeStamp.Stamp[0, 0, BcdCreateTime[]]]};


 -- new version stamp operations
 
  StampSize: NAT = 3;
  Stamp: PUBLIC TYPE = RECORD [word: ARRAY [0..StampSize) OF CARDINAL];
  
  AddStamps: PROC [s1, s2: Stamp] RETURNS [sum: Stamp] = {
    carry: [0..1] ← 0;
    i: NAT;
    FOR i DECREASING IN [0..StampSize) DO
      t: Inline.LongNumber ← [lc[LONG[s1.word[i]] + LONG[s2.word[i]] + LONG[carry]]];
      sum.word[i] ← t.lowbits;  carry ← t.highbits;
      ENDLOOP;
    FOR i DECREASING IN [0..StampSize) WHILE carry # 0 DO
      t: Inline.LongNumber ← [lc[LONG[sum.word[i]] + LONG[carry]]];
      sum.word[i] ← t.lowbits;  carry ← t.highbits;
      ENDLOOP};
    
  RotateStamp: PROC [s: Stamp] RETURNS [Stamp] = INLINE {RETURN [AddStamps[s, s]]};
  
  MergeStamps: PUBLIC PROC [sum, item: Stamp] RETURNS [Stamp] = {
    RETURN [AddStamps[RotateStamp[sum], item]]};
    

 -- exception processing

  SignalArgs: PUBLIC PROC RETURNS [signal, message: UNSPECIFIED] = {
    RETURN [0, 0]};


 -- file interface
 
  FileError: PUBLIC ERROR [name: LONG STRING] = CODE;
  
  FindFile: PUBLIC PROC [name: LONG STRING, access: OSMiscOps.FileAccess]
      RETURNS [File.Capability] = {
    file: File.Capability;
    permissions: File.Permissions = OSMiscOps.Permissions[access];
    old: BOOL ← (permissions = File.read);
    IF ~old THEN { 
      file ← Directory.CreateFile[name, DCSFileTypes.tLeaderPage, 0
	      ! Directory.Error => {
		 IF type = fileAlreadyExists THEN GOTO fileExists 
		 ELSE GO TO fileProblem}];
      EXITS
        fileExists => old ← TRUE};
    IF old THEN
       file ← Directory.Lookup[fileName: name, permissions: Directory.ignore
	! Directory.Error => {GO TO fileProblem}];
    RETURN [Directory.UpdateDates[file, permissions]]
    EXITS
      fileProblem => ERROR FileError[name]};
   
  RenameFile: PUBLIC PROC [newName, oldName: LONG STRING] = {
    DeleteFile[newName];	-- in case one already exists
    Directory.Rename[oldName, newName, Directory.defaultContext
      ! Directory.Error => {GOTO fileProblem}]
    EXITS
      fileProblem => ERROR FileError[oldName]};
    
  UnnameFile: PUBLIC PROC [oldName: LONG STRING, file: File.Capability] = {
    IF file = File.nullCapability THEN
      file ← Directory.Lookup[fileName: oldName, permissions: Directory.fileMaxPermissions
	! Directory.Error => {GOTO fileProblem}];
    KernelFile.MakeTemporary[file];
    Directory.RemoveFile[oldName, file
      ! Directory.Error => {GOTO fileProblem}];
    EXITS
      fileProblem => NULL};

  DeleteFile: PUBLIC PROC [name: LONG STRING] = {
    -- this is an enormously robust delete routine!
    IF name # NIL THEN {
      OPEN Directory;
      failed: BOOL ← FALSE;
      file: File.Capability;
      file ← Directory.Lookup[name, defaultContext, fileMaxPermissions
             ! ANY => {failed ← TRUE; CONTINUE}];
      IF ~failed THEN
        Directory.DeleteFile[name
          ! ANY => {Directory.RemoveFile[name, file ! ANY => {CONTINUE}]}]}}; 

  }.