{$X+,B-,V-} {essential compiler directives}

Unit nwFile;

{ nwFile unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk }

INTERFACE
{ Primary Functions                     Interrupt: comments:

Volume Management (Volume Tables)
---------------------------------

* ClearObjectVolRestriction             (F216/22)  (3) [aka ClearVolumeRestrictions/RemoveObjectDiskRestrictions]
* GetObjectVolRestriction               (F216/29)  (3) [aka GetObjDiskRestrictions/GetObjectDiskUsageAndRestrictions]
* GetVolumeName                         (F216/06)
* GetVolumeNameWithHandle               (F216/15)      [aka GetVolumeInfoWithHandle]
* GetVolumeNumber                       (F216/05)
* GetVolumeUsage                        (F216/2C)  (3) [aka GetExtendedVolumeInformation]
* IsVolumeRemovable                     (F212)         [aka GetVolumeInfoWithNumber]
* ScanVolForRestrictions                (F216/20)  (3)
* SetObjectVolRestriction               (F216/21)  (3) [aka SetVolumeRestrictions/SetObjectVolSpaceLimit
                                                            /AddUserDiskspaceRestriction]

Directory Handles (Directory Handle Table/Drive tables)
-------------------------------------------------------

* AllocPermanentDirHandle               (F216/12)
* AllocTemporaryDirHandle               (F216/13)
* DeallocateDirHandle                   (F216/14)
* DeleteFakeRootDirectory               (E906)
* GetDirectoryHandle                    (E900)
* GetDriveConnectionId                  (EF02)
* GetDirectoryPath                      (F216/01)
* GetDriveFlag                          (EF01)     (6)
* GetDriveHandle                        (EF00)     (6)
* GetRelativeDriveDepth                 (E907)
* GetSearchDriveVector                  (E901)
* MapFakeRootDirectory                  (E905)
* SetDirectoryHandle                    (F216/00)
* SetDriveConnectionId                  (EF02)
* SetDriveFlag                          (EF01)
* SetDriveHandle                        (EF00)
* SetSearchDriveVector                  (E902)

  Secondary Functions

* DeleteConnectionsDriveMappings
* DeleteDriveMapping
* GetEnvPath                            (BA..)
* IsSearchDrive                         (BA..)
* IsNetworkDrive                        (4409)
* MapDrive
* MapPermanentDrive
* MapSearchDrive
* SetEnvPath                            (BA..)

Entries (directory/file management)
-----------------------------------

* ChangeDirectory                       (3B..)     (DOS)
* ConvertPathToDirEntryId               (F217/F4)
* CreateDirectory                       (F216/0A)
* DeleteDirectory                       (F216/0B)
* EraseFiles                            (F244)
. FileServerFileCopy                    (F3..)
  GetDirectoryInfo                      (F216/2D)  (3)
* GetDirectoryEntry                     (F216/1F)  (3)
. GetExtendedFileAttributes             (B600)     =F24E ???
. GetFileAttributes                     (4300)     (DOS)
* GetTrueEntryName                      (60..)     (DOS)
* MapDirentryIdToPath                   (F217/F3)
  MoveEntry                             (F216/2E)  (3) dir and files
* PurgeSalvagableFile                   (F216/1D)  (3)
* RecoverSalvagebleFile                 (F216/1C)  (3)
* RenameDirectory                       (F216/0F)
* ScanDirectoryInformation              (F216/02)
* ScanDirectoryEntry                    (F216/1E)  (3)
* ScanFileInformation                   (F217/0F)
  ScanFilePhysical                      (F216/28)  (3)
* ScanSalvagableFiles                   (F216/1B)  (3)
* SetEntry                              (F216/25)  (3) dir and files
. SetExtendedFileAttributes             (B601)     =F24F
. SetFileAttributes                     (F246)     [4301]
* SetFileInformation                    (F217/10)

* ScanDirRestrictions                   (F216/23)  (3)
* SetDirRestriction                     (F216/24)  (3)

  Secondary functions:

  DeleteFile
  GetFileHandle
  IsFileShareable
  FlagFileShareable
  PurgeFiles          (by dirHandle,fileMask)
  SalvageFiles        (by dirHandle,fileMask)
  PurgeAllErasedFiles


Trustees/Max. Rights Mask
-------------------------

* DeleteTrustee                         (F216/2B)  (3)
* GetEffectiveRights                    (F216/2A)  (3)
. ModifyMaximumRightsMask               (F216/04)
. ScanBinderyObjectTrusteePaths         (F217/47)
* ScanEntryForTrustees                  (F216/26)  (3)
* SetTrustee                            (F216/27)  (3)


Not Implemented:
----------------

- AddTrusteeToDirectory                 (F216/0D)  (10)
- AllocSpecialDirHandle                 (F216/16)  (2)
- DeleteTrusteeFromDirectory            (F216/0E)  (10)
- FileServerFileCopy                    (E6..)     (8)
- GetEffectiveDirectoryRights           (F216/03)  (10)
- GetPathFromDirEntryID                 (F216/1A)  (12)
- GetVolumeInformation                  (F217/E9)  (1)
- GetVolumeInfoWithHandle               (F216/15)  (5)
- GetVolumeInfoWithNumber               (F212)     (4) [DA..]
- PurgeErasedFiles                      (F216/10)  (8)
- PurgeAllErasedFiles                   (F217/CE)  (8)
- RestoreDirectoryHandle                (F216/18)  (2)
- RestoreErasedFile                     (F216/11)  (8)
- SaveDirectoryHandle                   (F216/17)  (2)
- ScanDirectoryForTrustees              (F216/0C)  (9)
- SetDirectoryInformation               (F216/19)  (11)
- SetFileAttributes                     (E4..)     (7)
- UpdateFileSize                        (E5..)     (7)


Notes: (1) GetVolumeInformation. This call is NOT available in all 3.x versions.
           (only with Nw 2.1 & 3.1x and CLIB.NLM dated before 11-11-92 )
           This call is not implemented here. Replaced by GetVolumeUsage.
       (2) not available in (all versions of) NW 3.x.
       (3) NW 3.x (and upwards) only.
       (4) Replaced by GetVolumeUsage and IsVolumeRemovable.
       (5) Replaced by GetVolumeUsage and GetVolumeNameWithHandle.
       (6) Information can also be obtained by calling GetDirectoryHandle.
       (DOS) 'Normal' DOS call, extended by NetWare shell.
       (7) Not supported by Adv.NW 3.x. Not implemented here.
           These are functions using FCB's. If another function with the same
           name is listed here, that function performs the same action.
       (8) Not supported by Adv.NW 3.x. Not implemented here.
           These functions have been replaced with calls marked (3)
       (9) Replaced by a newer version: ScanEntryForTrustees.
       (10) Replaced by DeleteTrustee, GetEffectiveRights and SetTrustee.
       (11) Replaced by SetEntry
       (12) Replaced by MapDirEntryIDtoPath

       }

Uses nwIntr,nwMisc,nwBindry,nwConn;

Var Result:Word;

Type TsearchDriveVector=array [1..17] of byte;


CONST
  DRIVE_UNUSED    = $00;
  DRIVE_PERMANENT = $01; { Drive permanently assigned to fileserver directory }
  DRIVE_TEMPORARY = $02; { Drive temporary assigned to FS dir. Released by EOJ }
  DRIVE_NETWORK   = $03; { Normal drive mapping }
  DRIVE_LOCAL     = $80; { Drive is local. ! By ORing with one of the above bits,
                           it can be reassigned to a FS directory.}

  {Name Space Type constants}
  NS_DOS     =0;
  NS_MAC     =1;
  NS_NFS     =2;
  NS_FTAM    =3;
  NS_HPFS    =4;

  { Attributes / Netware directory & file attributes }
  A_NORMAL         =     $00; {file}
  A_READ_ONLY      =     $01; {file}
  A_HIDDEN         =     $02; {file/dir}
  A_SYSTEM         =     $04; {file/dir}
  A_EXECUTE_ONLY   =     $08; {file}
  A_DIRECTORY      =     $10; {file}
  A_NEEDS_ARCHIVED =     $20; {file}
  A_undocumented   =     $40;
  A_SHAREABLE      =     $80; {file}

  A_LO_SEARCH      =   $0100; {file}
  A_MID_SEARCH     =   $0200; {file}
  A_HI_SEARCH      =   $0400; {file}
  A_RESERVED       =   $0800; {file/dir}
  A_TRANSACTIONAL  =   $1000; {file}
  A_INDEXED        =   $2000; {file}
  A_READ_AUDIT     =   $4000; {file}
  A_WRITE_AUDIT    =   $8000; {file}

  A_PURGE          = $010000; {file/dir}
  A_RENAME_INHIBIT = $020000; {file/dir}
  A_DELETE_INHIBIT = $040000; {file/dir}
  A_COPY_INHIBIT   = $080000; {file}

  { Trustee Attributes / directory access rights }
  TA_NONE       = $00;
  TA_READ       = $01; {R open/read}
  TA_WRITE      = $02; {W open/write}
  TA_RESERVED   = $04; {  reserved, set to 0 }
  TA_CREATE     = $08; {C create files or dirs}
  TA_DELETE     = $10; {E delete files/dirs}
  TA_ACCESS     = $20; {A set /delete trustees}
  TA_SEARCH     = $40; {F directory can be searched/file is visible}
  TA_MODIFY     = $80; {M modify dir/file attributes}
  TA_SUPERVISOR =$100; {S supervisor rights to file or directory }

  { Entry Modify flags / see SetEntry }

  EM_ENTRYNAME      = $00000001;
  EM_ATTRIBUTES     = $00000002;
  EM_CREATIONTIME   = $0000000C; { date = $04, time = $08 }
  EM_OWNERID        = $00000010;
  EM_ARCHIVETIME    = $00000060; { date = $20, time = $40 }
  EM_ARCHIVERID     = $00000080;
  EM_MODIFYTIME     = $00000300; { date = $0100, time =$0200 }
  EM_MODIFIERID     = $00000400;
  EM_LASTACCESSTIME = $00000800; { date = $0800 }
  EM_RIGHTSMASK     = $00001000;
  EM_MAXDISKSPACE   = $00002000;

Type TvolUsage=record
               totalBlocks,                    {static info}
               freeBlocks,                     {dynamic}
               purgableBlocks,                 {dynamic}
               notYetPurgableBlocks,           {dynamic}
               totalDirEntries,                {static}
               availDirEntries,                {dynamic}
               Flags               :LongInt;   {dynamic}
               SectorsPerBlock     :byte;      {static/number of 512 byte sectors per block}
               volumeName          :string[16];{static}
               end;

     { used By ScanVolForRestrictions }
     TobjVolRestr=array[1..64] of record
                                  objId           :LongInt;
                                  MaxAllowedBlocks:LongInt;
                                  end;


Type Tentry=record
            EntryName       :String[16];

            NSType          :byte;       {namespace number}
            DataForkSize    :Longint;    { =FileSize when NStype=0 (dos) }
           {ResourceForkSize:Longint;    (Mac data) =0 when NStype=0 (dos)  }
            FileSize        :Longint;    {FileSize=Resource+Data forksize }

            Attributes      :Longint;
            RightsMask      :word;       {(4)}

            CreationTime,
            ArchiveTime,
            ModifyTime,
            LastAccessTime,
            DeleteTime      :TnovTime;   {salvagable file only}

            OwnerId,
            ArchiverId,
            ModifierId,
            DeletorId       :Longint;    {salvagable file only}

            end;
     { Note: (4) When used with ScanDirectoryInfo, this field
                 contains the MaximumRightsMask.
                 Otherwise, the InheritedRightsMask }

Type TdirRestrList=array[1..56] of record
                                   Level:Byte;
                                   MaxBlocks,
                                   AvailableBlocks:Longint;
                                   end;
     {when MaxBlocks and Availableblocks are set to to $7FFFFFFF,
      no restrictions are enforced -at this level-}

Type TtrusteeInformation=record
                         NumberOfTrustees:Byte;
                         TrusteeID    :array[1..20] of Longint;
                         TrusteeRights:array[1..20] of Word;
                         end;

{-------------------- Volumes----------------------- }
{F216/05 [2.15c+]}
Function GetVolumeNumber( volumeName:String; Var volumeNumber:Byte ):boolean;
{ Returns the volume number of a given volume name }

{F216/06 [2.15c+]}
Function GetVolumeName( volumeNumber:Byte; Var volumeName:String ):boolean;
{ Returns the volume name of a give volume number [0..31].
  If the volume is not mounted at the time of this call, a null-string is returned. }

{F216/2C [2.15c+]}
Function GetVolumeUsage(volumeNumber:byte; Var VolUsage: TvolUsage):boolean;

{F212 [2.15c+]}
Function IsVolumeRemovable( volumeNumber:Byte;
                            Var volIsRemoveable:Boolean):boolean;

{F216/15 [2.15c+]}
Function GetVolumeNameWithHandle( dirHandle:Byte;
                                  Var volumeName:String ):boolean;
{F216/29 [3.x]}
Function GetObjectVolRestriction(VolumeNumber:byte; objId:LongInt;
                  Var MaxAllowedBlocks,BlocksInUse:LongInt):boolean;

{F216/21 [3.x]}
Function SetObjectVolRestriction(VolumeNumber:byte; objId,
                                 MaxAllowedBlocks:LongInt):boolean;
{F216/22 [3.x]}
Function ClearObjectVolRestriction(VolumeNumber:byte; objId:LongInt):boolean;


{F216/20 [3.x]}
Function ScanVolForRestrictions(VolumeNumber:byte;
                           {i/o} Var sequenceNbr:LongInt;
                           {out} Var NbrOfObjects:byte;
                                 Var ResultBuffer:TobjVolRestr):boolean;
{ 1st call: sequenceNbr=0,
  after last call: sequenceNbr=0 again. }

{-------------------- Directory Handles/ Drives -------------}

{F216/01}
Function GetDirectoryPath(DirHandle:byte; Var PathName:string):boolean;

{EF00 [2.0/2.1/3.x]}
Function GetDriveHandle( DriveNumber:Byte; Var DirHandle:Byte ):boolean;
{ The call returns a pointer to the shell's Drive Handle Table. (32 bytes)
  (Drives A..Z and temporary drives [\]^_' )
  If a drive has been assigned a directory handle on the file server,
  the handle can be found in the DHT at the position corresponding with the drive letter.}

{EF00 [2.0/2.1/3.x]}
Function SetDriveHandle( DriveNumber:Byte; DirHandle:Byte ):boolean;

{E900 [2.0/2.1/3.x]}
Function GetDirectoryHandle( DriveNumber:Byte; Var dirHandle,status:byte):Boolean;
{ Returns directory handle and status flags for a drive.                   }
{ Drivenumber = 0..31 (A..Z = 0..25) and temp drives (26..31)              }

{EF01 [2.0/2.1/3.x]}
Function GetDriveFlag( DriveNumber:Byte; Var DriveStatus:Byte ):Boolean;
{ This call returns a pointer to the shell's Drive Flag Table (32 Bytes)
  Each entry indicates a drive's status (permanent,temporary,local,unassigned)
  For further explanation see the DRIVE_xxx constants.}

{EF01 [2.0/2.1/3.x]}
Function SetDriveFlag( DriveNumber:Byte; DriveStatus:Byte ):Boolean;

{F216/14 [2.15c+]}
function DeallocateDirHandle(DirHandle : Byte) : Boolean;
{ This function deletes a directory handle }


{EF02 [2.0/2.1/3.x]}
Function GetDriveConnectionID( DriveNumber:Byte; Var connID:Byte):boolean;
{ returns the servernumber (1..8) associated with a drive. }

{EF02 [2.0/2.1/3.x]}
Function SetDriveConnectionID( DriveNumber:Byte; connID:Byte):boolean;

{F216/00 [2.15c+]}
Function SetDirectoryHandle( sourceDirHandle:Byte; sourceDirPath:String;
                             targetDirHandle:Byte ):boolean;
{ make handle 'targetHandle' point to the directory provided by
  sourceHandle and/or sourceDirPath.                                      }

{F216/12 [2.15c+]}
FUNCTION AllocPermanentDirHandle( DriveNumber:Byte;
                                  DirHandle : byte; DirPath : string ;
                                  var NewDirHandle, EffectiveRights: byte ) :boolean;

{F216/13 [2.15c+]}
function AllocTemporaryDirHandle( DriveNumber:byte;
                                  DirHandle : Byte; DirPath : String;
                              var NewDirHandle,EffectiveRights : Byte) : Boolean;
{ Allocates a temporary directory handle, deleted automatically by EOJ. }

{E901}
Function GetSearchDriveVector(Var vector:TsearchDriveVector):boolean;

{E902 }
Function SetSearchDriveVector(vector:TsearchDriveVector):boolean;

{E905 (shell 3.01+)}
Function MapFakeRootDirectory(DriveNumber:byte; DirPath:string):boolean;

{E906 (shell 3.01+)}
Function DeleteFakeRootDirectory(DriveNumber:byte):boolean;

{E907 (shell 3.01+)}
Function GetRelativeDriveDepth(DriveNumber:byte; Var depth:byte):boolean;

{BA.. }
Function GetEnvPath(Var EnvPath:string):boolean;

{BA.. }
Function SetEnvPath(EnvPath:string):boolean;


{secondary }
FUNCTION MapDrive(DriveNumber:byte; DirectoryPath:string;
                  Root, Permanent:boolean):boolean;

{secondary }
FUNCTION MapPermanentDrive(DriveNumber:byte; DirectoryPath:string;
                           Root:boolean):boolean;

{secondary}
Function MapSearchDrive(DriveNumber:byte; DirPath:string;
                        PathPosition:byte;
                        Insert,Root,Permanent:Boolean):boolean;

{secondary}
Function DeleteDriveMapping(DriveNumber:Byte):boolean;

{secondary}
Function DeleteConnectionsDriveMappings(ConnId:Byte):Boolean;

{secondary}
Function IsSearchDrive(DriveNumber:byte):boolean;

{4409 }
Function IsNetworkDrive(driveNumber:Byte):boolean;
{ isNetworkDrive is set to TRUE if the drive is a) a network drive, and
  b) a legal drive letter was used.                                         }


{------------------------- entries -----------------------------------------}

{F217/0F [2.15c+]}
Function ScanFileInformation(DirHandle:Byte; FilePath:string;
                             SearchAttrib:Byte;
                        {i/o} VAR SequenceNbr:Integer;
                        {out} VAR fileInfo:Tentry):Boolean;

{F217/F4 [3.0+]}
Function ConvertPathToDirEntryId(dirHandle:Byte; dirPath:string;
                             Var VolNbr    :byte;
                             Var dirEntryID:LongInt):boolean;
{ aka ConvertPathToDirEntry / requires console rights }

{F216/02}
Function ScanDirectoryInformation(dirHandle:byte; searchDirPath:string;
                            {i/o} Var sequenceNumber:word;
                           {out:} Var dirInfo:Tentry    ):boolean;

{F216/1F [2.15c+]}
Function GetDirectoryEntry(DirHandle:byte;
                           Var dirEntry:Tentry):boolean;

{F216/1E [2.15c+]}
Function ScanDirectoryEntry(DirHandle:Byte; EntryName:string; SearchFlags:Longint;
                      {i/o} Var EntryId:Longint;
                      {out} Var Entry:Tentry    ):boolean;

{F217/F3 [3.0+]}
Function MapDirEntryIdToPath(VolNbr:byte;DirEntryId:Longint; NStype:byte;
                             Var ExtPath:string):boolean;

{F216/25 [2.15c+] }
Function SetEntry(DirHandle:Byte;EntryId:Longint;SearchFlags:Byte;
                  ModFlags:Longint; Entry:Tentry                ):boolean;

{F217/10 [2.15c+]}
Function SetFileInformation(DirHandle:Byte; FilePath:string;
                            SearchAttrib:Byte;
                            fileInfo:TEntry):boolean;

{F216/1B [2.15c+]}
Function ScanSalvagableFiles(DirHandle:Byte;
                      {i/o}  Var EntryId:Longint;
                      {out}  Var Entry:Tentry   ):boolean;

{F216/1D  [3.0+]}
Function PurgeSalvagableFile(DirHandle:Byte;
                             EntryId:Longint; FileName:string):boolean;

{F216/1C [3.0+] }
Function RecoverSalvagableFile(dirHandle:Byte; EntryId:Longint;
                               OldName,NewName:string):boolean;

{F244 [2.1x/3.x]}
Function EraseFiles(dirHandle, searchAttrib:Byte; filePath:string ):boolean;

{60.. (extended DOS call)}
Function GetTrueEntryName(DirPath:string; Var CanonicalPath:string):boolean;


{F216/0F [2.0/2.1/3.x]}
Function RenameDirectory( dirHandle:Byte; dirPath, newDirName :String):Boolean;

{F216/0B [2.15c+]}
Function DeleteDirectory(DirHandle:Byte; DirPath:string):boolean;

{F216/0A [2.15+]}
Function CreateDirectory(DirHandle:Byte; DirPath:string; MaxRightsMask:byte):boolean;

{3B.. }
Function ChangeDirectory(DirPath:string):boolean;


{F216/24 [3.0+]}
Function SetDirRestriction(DirHandle:Byte; DiskSpaceLimit:Longint):boolean;

{F216/23 [3.0+]}
Function ScanDirRestrictions(DirHandle:Byte;
                           Var NumberOfEntries:Byte;
                           Var RestrInfo:TdirRestrList):boolean;

{--------------------------- Rights/trustees ---------------------------}

{F216/27 [3.0+]}
Function SetTrustee(DirHandle:Byte;DirPath:string;
                    TrusteeObjectID:Longint;
                    RightsMask:Word               ):boolean;

{F216/2B [3.0+]}
Function DeleteTrustee(DirHandle:Byte;DirPath:String;
                       TrusteeObjectId:Longint):boolean;

{F216/2A [3.0+]}
function GetEffectiveRights(DirHandle:Byte;DirPath:String;
                        var Rights:Word) : Boolean;

{F216/04 [2.15c+]}
Function ModifyMaximumRightsMask(DirHandle:Byte;DirPath:string;
                                 RevokeRightsMask,GrantRightsMask:Word):boolean;


{F217/47 [2.15c+]}
Function ScanBinderyObjectTrusteePaths(TrusteeObjectId:Longint;
                                       VolumeNumber:Byte;
                             {i/o} Var SequenceNumber:word;
                             {out} Var AccessMask:Word;
                                   Var Path:string        ):boolean;

{F216/26 [3.0+]}
Function ScanEntryForTrustees(DirHandle:Byte;DirPath:String;
                    {i/o} Var SequenceNumber:Byte;
                    {out} Var TrusteeInfo: TtrusteeInformation):boolean;

IMPLEMENTATION{============================================================}

{$IFDEF MSDOS}
uses dos; { file handles / 'normal' file attributes }
{$ENDIF}

Type TintEntry=record  { Unit internal Entry type }
         {  0} _res1          :Longint; { low word = Dir Id of parent Dir }
         {  4} _attrib        :Longint;
         {  8} _res2          :word;
         { 10} _NStype        :Byte;
         { 11} _name          :string[12];
         { 24} _creationTime  :Longint;
         { 28} _OwnerId       :Longint;    { hi-lo}
         { 32} _ArchiveTime   :Longint;
         { 36} _ArchiverId    :Longint;    { hi-lo}
         { 40} _modifyTime    :Longint;

         { 44} _ModifierId    :Longint;               { files only }
         { 48} _ForkSize      :Longint;               { files only }
         { 52} _res3          :array[1..44] of byte;  { Trustee obj IDs and Tr. rights }
         { 96} _FileRightsMask:word;                  { files only }
         { 98} _AccessDate    :word;                  { files only }

         {100} _DirRightsMask :word;                  { directories only }
         {102} _res4          :word; {Unique Dir ID, hi-lo} { directories only }
         {104} _DeleteTime    :Longint;               { salvageable files only }
         {108} _DeletorID     :LongInt;               { salvageable files only }
         {112} _res5          :array[1..16] of byte;
         {128} end;

Procedure Convert2ExtEntry(Var Ie:TintEntry;Var Oe:Tentry);
begin
FillChar(Oe,Sizeof(Tentry),#$0);
with Ie,Oe
 do begin
    Attributes:=_Attrib;
    NStype:=_NStype;
    Entryname:=_name;
    DosTime2NovTime(_CreationTime,CreationTime);
    OwnerId:=Lswap(_OwnerId); {force lo-hi}
    DosTime2NovTime(_ArchiveTime,ArchiveTime);
    ArchiverId:=Lswap(_ArchiverID); {force lo-hi}
    DosTime2NovTime(_ModifyTime,ModifyTime);
    if (_attrib and $10)>0 { is entry a directory ? }
     then begin
          RightsMask:=_DirRightsMask;
          end
     else begin
          ModifierId:=LSwap(_ModifierId); {force lo-hi}
          DataForksize:=_Forksize;
          if _NSType=0
           then FileSize:=_ForkSize;
          RightsMask:=_FileRightsMask;
          DosTime2NovTime(MakeLong(_accessDate,0),LastAccessTime);
          DosTime2NovTime(_DeleteTime,DeleteTime);
          DeletorId:=Lswap(_DeletorID); {force lo-hi}
          end;
    end;
end;

Procedure Convert2IntEntry(Var Oe:TEntry;Var Ie:TIntEntry);
Var TempTime:Longint;
begin
FillChar(Ie,Sizeof(Tentry),#$0);
with Ie,Oe
 do begin
    _Attrib:=Attributes;
    _NStype:=NStype;
    _Name:=EntryName;
    NovTime2DosTime(CreationTime,_CreationTime);
    _OwnerId:=Lswap(OwnerId); {force hi-lo}
    NovTime2DosTime(ArchiveTime,_ArchiveTime);
    _ArchiverId:=Lswap(ArchiverId); {force hi-lo}
    NovTime2DosTime(ModifyTime,_ModifyTime);
    if (Attributes and $10)>0 { is entry a directory ? }
     then begin
          _DirRightsMask:=RightsMask;
          end
     else begin
          _ModifierId:=Lswap(ModifierId); { force hi-lo }
          _ForkSize:=DataForkSize;
          _FileRightsMask:=RightsMask;
          NovTime2DosTime(LastAccessTime,TempTime);
          _AccessDate:=HiLong(TempTime);
          NovTime2DosTime(DeleteTime,_DeleteTime);
          _DeletorID:=Lswap(DeletorId); { force hi-lo }
          end;
    end;
end;

Procedure ConvertPathToVolFormat(Var path:string);
{ reformat \\server\vol\path to VOL:PATH
           server/vol:path   to VOL:PATH  }
Var pcolon,pslash:byte;
begin
if (Path[0]>#1) and (Path[1]='\') and (Path[2]='\')
 then begin
      delete(Path,1,2);
      Path:=Path+'\';
      pslash:=pos('\',Path);
      if pslash>0
       then begin
            delete(Path,1,pslash); { remove servername from path }
            pslash:=pos('\',Path);
            if pslash>0
             then Path:=copy(Path,1,pslash-1)+':'+copy(Path,pslash+1,255);
            end;
       while Path[ord(Path[0])]='\' do dec(Path[0]);
       end
 else begin
      pcolon:=pos(':',path);
      if (path[0]>#3) and (pcolon>3)
       then begin
            pslash:=pos('/',path);
            if (pslash=0) or (pslash>pcolon)
             then pslash:=pos('\',path);
            if (pslash>0) and (pslash<pcolon)
             then delete(path,1,pslash); { remove 'server/' }
            end;
      end;
end;


{------------------ Volume Management --------------------------------------}


{F216/2C [2.15c+]}
Function GetVolumeUsage(VolumeNumber:byte; Var VolUsage: TvolUsage):boolean;
Type Treq=record
          len:word;
          subf:byte;
          volNbr:byte;
          end;
     Trep=TvolUsage;
     TPreq=^Treq;
     TPrep=^Trep;
begin
with TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subf:=$2C;
    volNbr:=VolumeNumber;
    end;
F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result);
VolUsage:=TPrep(GlobalReplyBuf)^;
IF (Volusage.totalBlocks=0)
   or (volusage.volumename='')
 then result:=$98;
GetVolumeUsage:=(result=0);
{resultcodes:
  00 success; 98h Volume doesn't exist / not mounted }
end;


{F216/06 [2.15c+]}
Function GetVolumeName(volumeNumber:Byte; Var volumeName:String ):boolean;
{ Returns the volume name of a give volume number [0..31].
  If the volume name doesn't exist, or the volume is not mounted
  at the time of this call, an error $98 results and volName is set to '' }
Type Treq=record
          len     :word;
          subFunc :byte;
          volNbr :Byte;
          end;
     Trep=record
          _volName:String[16];
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$06;
    volNbr:=volumeNumber
    end;
F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result);
volumeName:=TPrep(GlobalReplyBuf)^._volName;
if volumeName=''
 then result:=$98;
GetVolumeName:=(result=0)
{resultcodes:
  00 success; 98h Volume doesn't exist / not mounted }
end;



{F216/05 [2.15c+]}
Function GetVolumeNumber( volumeName:String; Var volumeNumber:Byte ):boolean;
{ Returns the volume number [0..31] of a given volume name.
  The volumename can have a maximum of 16 characters. Wildcards are
  allowed, the volume number of the first match will be returned.
  The name of the volume may be supplied with ('SYS:') or without
  the colon ('SYS'). }
Type Treq=record
          len      :word;
          subFunc  :byte;
          volName  :String[16];
          end;
     Trep=record
          volNbr  :Byte;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    subFunc:=$05;
    if volumeName[0]>#16
     then volumeName[0]:=#16;
    volName:=volumeName;
    if volname[ord(volName[0])]=':'
     then dec(volName[0]);
    len:=2+ord(volName[0]);
    F2SystemCall($16,len+2,SizeOf(Trep),result);
    end;
volumenumber:=TPrep(GlobalReplyBuf)^.volNbr;
getVolumeNumber:=(result=0)
{resultcodes:
  00 success; 98h volume doesn't exist }
end;


{F216/15 [2.15c+]}
Function GetVolumeNameWithHandle( dirHandle:Byte;
                                  Var volumeName:String ):boolean;
Type Treq=record
          len        :word;
          subFunc    :byte;
          _dirHandle :Byte;
          end;
     Trep=record
          _sectPerBlock :Word; {hi-lo}
          _TotalBlocks  :Word; {hi-lo}
          _availBlocks  :Word; {hi-lo} { Use GetVolumeUsage for the other fields }
          _TotalDirSlots:Word; {hi-lo}
          _availDirSlots:Word; {hi-lo}
          _volName      :array[1..16] of byte;
          _volRemoveable:Word; {hi-lo}
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$15;
    _dirHandle:=dirHandle;
    end;
F2SystemCall($16,Sizeof(Treq),SizeOf(Trep),result);
ZStrCopy(volumeName,TPrep(GlobalReplyBuf)^._volName,16);
if volumeName=''
 then result:=$9B; { Invalid directory handle }
getVolumeNameWithHandle:=(result=0)
{ resultcodes: 00 success; $9B invalid directory handle }
end;


{F212 [2.15c+]}
Function IsVolumeRemovable( volumeNumber:Byte;
                            Var volIsRemoveable:Boolean):boolean;
{ stripped down version of the GetVolumeInfoWithNumber call }
Type Treq=Byte;
     Trep=record
          _sectPerBlock  :Word; {hi-lo}
          _TotalBlocks   :Word; {hi-lo}
          _availBlocks   :Word; {hi-lo}
          _TotalDirSlots :Word; {hi-lo}
          _availDirSlots :Word; {hi-lo}
          _volName       :array[1..16] of byte;
          _volRemoveable :Word; {hi-lo}
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
TPreq(GlobalReqBuf)^:=volumeNumber;
F2SystemCall($12,SizeOf(Treq),SizeOf(Trep),result);
With TPrep(GlobalReplyBuf)^
 do begin
    volIsRemoveable:=(_volRemoveable>0);
    if _volName[1]=0
     then result:=$98;
    end;
IsVolumeRemovable:=(result=0);
{ resultcodes: 00 success; 98h Invalid volume number / volume not mounted }
end;

{F216/22 [3.x]}
Function ClearObjectVolRestriction(VolumeNumber:byte; objId:LongInt):boolean;
{ If the objId doesn't exist, no error is returned. }
Type Treq=record
          len:word;
          subFunc:byte;
          _volNbr:byte;
          _objId:LongInt; { hi-lo }
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$22;
    _volNbr:=VolumeNumber;
    _objId:=Lswap(objId); { force hi-lo }
    end;
F2SystemCall($16,SizeOf(Treq),0,result);
ClearObjectVolRestriction:=(result=0)
{ $8C No supervisor rights }
end;

{F216/29 [3.x]}
Function GetObjectVolRestriction(VolumeNumber:byte; objId:LongInt;
                  Var MaxAllowedBlocks,BlocksInUse:LongInt):boolean;
{ If MaxAllowedBlocks is equal to $40000000 on return, there are no
  disk restrictions for the object on this volume. }
{ You need not be logged in to use this call. }
Type Treq=record
          len    :word;
          subFunc:byte;
          _volNbr:byte;
          _objId :Longint; {hi-lo}
          end;
     Trep=record
          _MaxAllowedBlocks,
          _BlocksInUse      :Longint;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Var objName:string;
    objType:word;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len    :=SizeOf(Treq)-2;
    subFunc:=$29;
    _volNbr:=VolumeNumber;
    _objId :=Lswap(objId); {force hi-lo}
    end;
F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result);
With TPrep(GlobalReplyBuf)^
 do begin
    MaxAllowedBlocks:=_MaxAllowedBlocks;
    BlocksInUse:=_BlocksInUse;
    If BlocksInUse=0
     then if NOT nwBindry.GetBinderyObjectName(objId,objName,objType)
           then result:=$FF;
    end;
GetObjectVolRestriction:=(result=0)
{resultcodes: 00 success; $FF Invalid objectId }
end;

{F216/20 [3.x]}
Function ScanVolForRestrictions(VolumeNumber:byte;
                           {i/o} Var sequenceNbr:LongInt;
                           {out} Var NbrOfObjects:byte;
                                 Var ResultBuffer:TobjVolRestr):boolean;
{ 1st call: sequenceNbr=0,
  // n-th call: sequenceNbr(n):=sequenceNbr(n-1)+NbrOfObjects
  // (addition done by function itself)

  after last call: sequenceNbr=0 again. }
Type Treq=record
          len:word;
          subFunc:byte;
          _volNbr:byte;
          _seqNbr:LongInt; { lo-hi !}
          end;
     Trep=record
          _NbrOfObjects:byte;
          _buff        :TobjVolRestr;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Var t:byte;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$20;
    _volNbr:=VolumeNumber;
    _seqNbr:=sequenceNbr;
    end;
F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result);
if result=0
 then begin
      With TPrep(GlobalReplyBuf)^
       do begin
          NbrOfObjects:=_NbrOfObjects;
          ResultBuffer:=_buff;
          For t:=1 to NbrOfObjects
           do ResultBuffer[t].objId:=Lswap(_buff[t].ObjId);
          if _NbrOfObjects=0
           then result:=$FF
           else sequenceNbr:=sequenceNbr+_NbrOfObjects;
          end
      end
 else NbrOfObjects:=0;
ScanVolForRestrictions:=(result=0)
{ $98 VolumeNumber doesn't exist;
  $FF No New restriction data (end of iteration) }
end;

{F216/21 [3.x]}
Function SetObjectVolRestriction(VolumeNumber:byte; objId,MaxAllowedBlocks:LongInt):boolean;
{ If the objId doesn't exist, no error is returned. }
Type Treq=record
          len    :word;
          subFunc:byte;
          _volNbr:byte;
          _objId :Longint;    {hi-lo}
          _maxBlocks:LongInt; {lo-hi !!}
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalreqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$21;
    _volNbr:=VolumeNumber;
    _objId:=Lswap(objId); { force hi-lo }
    _maxBlocks:=MaxAllowedBlocks;
    end;
F2SystemCall($16,SizeOf(Treq),0,result);
SetObjectVolRestriction:=(result=0)
{ $8C No supervisor Rights }
end;


{--------------Dir handles/ drive mappings----------------------------------}


{BA.. }
Function GetEnvPath(Var EnvPath:string):boolean; {#d}
Type Tarr=array[1..2048] of byte;
Var regs:TTregisters;
    penv:^Tarr;
    i,envSize:word;
    state:byte;
begin
regs.ah:=$BA;
RealModeIntr($21,regs);
envSize:=byte(nwPtr(regs.dx-1,3)^) SHL 4;
penv:=nwPtr(regs.dx,0);
i:=1;
state:=0;
while (i<envSize) and (state<5)
 do begin
    CASE state of
     4:if penv^[i]=ord('=')
        then state:=5
        else state:=0;
     3:if penv^[i]=ord('H')
        then state:=4
        else state:=0;
     2:if penv^[i]=ord('T')
        then state:=3
        else state:=0;
     1:if penv^[i]=ord('A')
        then state:=2
        else state:=0;
     0:if penv^[i]=ord('P')
        then state:=1;
     end; {case}
     inc(i);
     end;

if state<5
 then begin
      result:=$300;
      GetEnvPath:=false;
      exit;
      end;
EnvPath:='';
while (i<=envSize) and (penv^[i]<>0)
 do begin
    EnvPath:=EnvPath+chr(penv^[i]);
    inc(i);
    end;
if i>envSize
 then begin
      result:=$301;
      GetEnvPath:=false;
      exit;
      end;
result:=0;
GetEnvPath:=true;
{ 00  successful
  300 'Path' not found
  301 Path value could not be read }
end;

{BA.. }
Function SetEnvPath(EnvPath:string):boolean; {#d}
Type Tarr=array[1..2048] of byte;
Var regs:TTregisters;
    penv:^Tarr;
    i,t,envSize:word;
    state:byte;
    pbegin,pend:word;
    NewPathSize,OldPathSize:byte;
    diff:integer;
    sVector:TsearchDriveVector;
    Vecind,p:byte;
    dn:Byte;
begin
Upstring(EnvPath);
If pos('PATH=',envPath)=1
 then delete(EnvPath,1,5);
regs.ah:=$BA;
RealModeIntr($21,regs);
envSize:=word(nwPtr(regs.dx-1,3)^) SHL 4;
penv:=nwPtr(regs.dx,0);

i:=1;
state:=0;
while (i<envSize) and (state<5)
 do begin
    CASE state of
     0:if penv^[i]=ord('P')
        then state:=1;
     1:if penv^[i]=ord('A')
        then state:=2
        else state:=0;
     2:if penv^[i]=ord('T')
        then state:=3
        else state:=0;
     3:if penv^[i]=ord('H')
        then state:=4
        else state:=0;
     4:if penv^[i]=ord('=')
        then state:=5
        else state:=0;
     end; {case}
     inc(i);
     end;

if state<5
 then begin
      result:=$300;
      SetEnvPath:=false;
      exit;
      end;
{ determine starting and ending character of path variable }
pbegin:=i;
while (i<envSize) and (penv^[i]<>0)
 do inc(i);
if i>envSize
 then begin
      result:=$301;
      SetEnvPath:=false;
      exit;
      end;
dec(i);
pend:=i;

{ determine end of 'active' environment / marked by $00 00}
while (i<envSize) and NOT ((penv^[i]=0) and (penv^[i+1]=0))
 do inc(i);
if i=envSize
 then begin
      result:=$301;
      SetEnvPath:=false;
      exit;
      end;
inc(i); { penv^[i] = 'second' 00, end-of-environment }

OldPathSize:=1+pend-pbegin;
NewPathSize:=ord(EnvPath[0]);

If (i+NewPathSize-OldPathSize)>envSize
 then begin
      result:=$302;
      SetEnvPath:=false;
      exit;
      end;

diff:=NewPathSize-OldPathSize;
if diff>0
 then for t:=i downto pend
      do penv^[t+diff]:=penv^[t];
if diff<0
 then for t:=pend to i
      do penv^[t+diff]:=penv^[t];
Move(EnvPath[1],penv^[pbegin],NewPathSize);

FillChar(Svector,SizeOf(TsearchDriveVector),#$FF);
VecInd:=1;
REPEAT
p:=pos(':',envPath);
if p>0
 then begin
      dn:=ord(ord(envPath[p-1])-ord('A'));
      p:=pos(';',envPath);
      if p=0
       then envPath:=''
       else delete(envPath,1,p);
      IF IsNetworkDrive(dn)
       then begin
            Svector[VecInd]:=dn;
            inc(VecInd)
            end;
      end;
UNTIL (p=0) or (VecInd=17);
SetSearchDriveVector(Svector);

result:=0;
SetEnvPath:=true;
{ 00  successful
  300 'Path' not found
  301 Environment failure
  302 Environment overflow (new path too large) }
end;


{F216/01}
Function GetDirectoryPath(DirHandle:byte; Var PathName:string):boolean;
{ path includes volumename }
Type Treq=record
          len    :word;
          subFunc:byte;
          _dh    :byte;
          end;
     Trep=record
          DirPath:string[255];
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$01;
    _dh:=DirHandle;
    end;
F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result);
PathName:=TPrep(GlobalReplyBuf)^.DirPath;
GetDirectoryPath:=(result=0)
{ 00 Successful      9B Bad directory handle }
end;

{EF02 [2.0/2.1/3.x]}
Function GetDriveConnectionID( DriveNumber:Byte; Var connID:Byte):boolean;
{ returns the servernumber (1..8) associated with a drive. }
Type pArr=^arr;
     arr=array[0..31] of byte;
Var regs:TTregisters;
begin
regs.ax:=$EF02;
RealModeIntr($21,Regs);
If DriveNumber>31
 then result:=$0105
 else begin
      connID:=Parr(nwPtr(Regs.es,regs.si))^[DriveNumber];
      Result:=0;
      end;
GetDriveConnectionID:=(Result=0);
end;

{EF02 [2.0/2.1/3.x]}
Function SetDriveConnectionID( DriveNumber:Byte; connID:Byte):boolean;
Type pArr=^arr;
     arr=array[0..31] of byte;
Var regs:TTregisters;
begin
regs.ax:=$EF02;
RealModeIntr($21,Regs);
If DriveNumber>31
 then result:=$0105
 else begin
      Parr(nwPtr(Regs.es,regs.si))^[DriveNumber]:=connId;
      Result:=0;
      end;
SetDriveConnectionID:=(Result=0);
end;


{EF00 [2.0/2.1/3.x]}
Function GetDriveHandle( DriveNumber:Byte; Var DirHandle:Byte ):boolean;
{ The call returns a pointer to the shell's Drive Handle Table. (32 bytes)
  (Drives A..Z and temporary drives [\]^_' )
  If a drive has been assigned a directory handle on the file server,
  the handle can be found in the DHT at the position corresponding with the drive letter.}
Type pArr=^arr;
     arr=array[0..31] of byte;
Var regs:TTregisters;
begin
regs.ax:=$EF00;
RealModeIntr($21,regs);
if DriveNumber>31
 then result:=$0105
 else begin
      DirHandle:=Parr(nwPtr(Regs.Es,Regs.Si))^[DriveNumber];
      Result:=0;
      end;
GetDriveHandle:=(Result=0);
end;

{EF00 [2.0/2.1/3.x]}
Function SetDriveHandle( DriveNumber:Byte; DirHandle:Byte ):boolean;
Type pArr=^arr;
     arr=array[0..31] of byte;
Var regs:TTregisters;
begin
regs.ax:=$EF00;
RealModeIntr($21,regs);
if DriveNumber>31
 then result:=$0105
 else begin
      Parr(nwPtr(Regs.Es,Regs.Si))^[DriveNumber]:=DirHandle;
      Result:=0;
      end;
SetDriveHandle:=(Result=0);
end;

{EF01 [2.0/2.1/3.x]}
Function GetDriveFlag( DriveNumber:Byte; Var DriveStatus:Byte ):Boolean;
{ This call returns a pointer to the shell's Drive Flag Table (32 Bytes)
  Each entry indicates a drive's status (permanent,temporary,local,unassigned)
  For further explanation see the DRIVE_xxx constants.}
Type pArr=^arr;
     arr=array[0..31] of byte;
Var regs:TTregisters;
begin
regs.ax:=$EF01;
RealModeIntr($21,Regs);
If DriveNumber>31
  then result:=$0105
  else begin
       DriveStatus:=Parr(nwPtr(Regs.es,regs.si))^[DriveNumber];
       Result:=0;
       end;
GetDriveFlag:=(Result=0);
end;

{EF01 [2.0/2.1/3.x]}
Function SetDriveFlag( DriveNumber:Byte; DriveStatus:Byte ):Boolean;
Type pArr=^arr;
     arr=array[0..31] of byte;
Var regs:TTregisters;
begin
regs.ax:=$EF01;
RealModeIntr($21,Regs);
If DriveNumber>31
  then result:=$0105
  else begin
       Parr(nwPtr(Regs.es,regs.si))^[DriveNumber]:=DriveStatus;
       Result:=0;
       end;
SetDriveFlag:=(Result=0);
end;


{E900 [2.0/2.1/3.x]}
Function GetDirectoryHandle( DriveNumber:Byte; Var dirHandle,status:byte):Boolean;
{ Returns directory handle and status flags for a drive.                   }
{ Drivenumber = 0..31 (A..Z = 0..25) and temp drives (26..31)              }
{ Status Byte
  7  6  5  4  3  2  1  0
  |                 |  +-Permenant Directory Handle
  |                 +----Temporary Directory Handle
  +----------------------Mapped to a local drive    }
{ in case of an invalid driveNumber, handle and status will be set to 0 }
Var Regs:TTRegisters;
begin
With Regs
do begin
   AX:=$E900;
   DX:=DriveNumber;
   RealModeIntr($21,Regs);
   { AH = Status Flags;
          01 mapped to a permanent dir handle;
          02 mapped to a temporary dir handle;
          80 local drive.                     }
   dirHandle:=AL;
   status:=AH;
   If dirHandle=0
    then begin status:=0;Result:=$FF end {INVALID_DRIVE_NUMBER}
    else Result:=0;
   GetDirectoryHandle:=(Result=0)
   end;
{ result: $00 success; $FF Invalid Drive Number }
end;


{F216/00 [2.15c+]}
Function SetDirectoryHandle( sourceDirHandle:Byte; sourceDirPath:String;
                             targetDirHandle:Byte ):boolean;
{ make handle 'targetHandle' point to the directory provided by
  sourceHandle and/or sourceDirPath. ( "Volume:dir\subdir" )   }
Type Treq=record
          len       :word;
          subFunc   :byte;
          _TargetDH :Byte;
          _SourceDH :Byte;
          _SourceDP :String[255]
          end;
    TPreq=^Treq;
Var p:Byte;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    subFunc:=$00;
    _TargetDH:=targetDirHandle;
    _SourceDH:=SourceDirHandle;
    if SourceDirHandle=0
     then ConvertPathToVolFormat(SourceDirPath);
    _sourceDP:=sourceDirPath;
    UpString(_sourceDP);
    len:=4+ord(_SourceDP[0]);
    F2SystemCall($16,len+2,0,result);
    end;
SetDirectoryHandle:=(result=0)
{ resultcodes:
  00 Success; 98h Volume does not exist;
  9Bh Bad directory handle; 9Ch Invalid Path. }
end;


{F216/12 [2.15c+]}
FUNCTION AllocPermanentDirHandle( DriveNumber:Byte;
                                  DirHandle : byte; DirPath : string ;
                              var NewDirHandle, EffectiveRights: byte ) :boolean;
{ Effective server must be the server involved, i.e. where the dir is stored }
Type Treq=record
          len : word;
          subf : byte;
          _dirHandle : byte;
          _driveLetter : char;
          _DirectoryPath: String[255];
          end;
     Trep=record
          _newDirHandle : byte;
          _EffectiveRights : byte; { e.r. mask }
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Var p:Byte;
BEGIN
With TPreq(GlobalReqBuf)^
 do begin
    subf := $12;
    _dirHandle := dirHandle;
    _driveLetter := chr(DriveNumber+ord('A'));
    if Dirhandle=0
     then ConvertPathToVolFormat(DirPath);
    _DirectoryPath:=DirPath;
    UpString(_DirectoryPath);
    len:=4+ord(_DirectoryPath[0]);
    F2SystemCall($16,len+2,sizeof(Trep),result);
    end;
if result = 0
 then with TPrep(GlobalReplyBuf)^
      do begin
         effectiveRights := _effectiveRights;
         newDirHandle    := _newDirHandle;
         end;
AllocPermanentDirHandle:=(result=0);
{ $00 Successful  $98 Volume doen't exist $9C Invalid path }
end;





{F216/13 [2.15c+]}
function AllocTemporaryDirHandle( DriveNumber:byte;
                             DirHandle : Byte; DirPath : String;
                         var NewDirHandle,EffectiveRights : Byte) : Boolean;
{ Allocates a temporary directory handle, deleted automatically by EOJ. }
{ Effective server must be the server involved, i.e. where the dir is stored }
Type TReq=record
          Len              : Word;
          SubF             : Byte;
          Handle           : Byte;
          Letter           : Char;
          _DirectoryPath   : String;
          end;
     TRep=record
          NewH    : Byte;
          Mask    : Byte;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Var p:Byte;
begin
with TPReq(GlobalReqBuf)^
 do begin
    SubF    := $13;
    Handle  := DirHandle;
    Letter  := chr(DriveNumber+ord('A'));
   { Allocating handles requires paths to be in
      the VOL:path format.. NOT canonical }
    if handle=0
     then ConvertPathToVolFormat(DirPath);
    _DirectoryPath:=DirPath;
    UpString(_DirectoryPath);
    Len:=4+length(_DirectoryPath);
    F2SystemCall($16,len+2,SizeOf(Trep),result);
    end;
with TPrep(GlobalReplyBuf)^
 do begin
    NewDirHandle    := NewH;
    EffectiveRights := Mask;
    end;
AllocTemporaryDirHandle:=(result=0);
{ result: 00 success; 98h Volume doesn't exist; 9Ch Invalid Path }
end;


{F216/14 [2.15c+]}
function DeallocateDirHandle(DirHandle : Byte) : Boolean;
{ This function deletes a directory handle }
Type TReq=record
          Len    : Word;
          SubF   : Byte;
          Handle : Byte;
          end;
     TPreq=^Treq;
begin
with TPReq(GlobalReqBuf)^
 do begin
    Len   := 2;
    SubF  := $14;
    Handle:= DirHandle;
    end;
F2SystemCall($16,Sizeof(Treq),0,result);
DeallocateDirHandle:=(result=0);
{ result:
  00h  - Success; 9Bh  - Bad directory handle }
end;


{E901 }
Function GetSearchDriveVector(Var vector:TsearchDriveVector):boolean;
Var regs:TTregisters;
    tmp1,tmp2:word;
begin
regs.ax:=$E901;
GetGlobalBufferAddress(tmp1,tmp2,regs.ds,regs.dx);
{ DS:DX real-mode address of GlobalReplyBuffer }
RealModeIntr($21,regs);
result:=0;
Move(GlobalReplyBuf^,vector,sizeof(TsearchDriveVector));
vector[17]:=$FF;
GetSearchDriveVector:=True;
end;

{E902 }
Function SetSearchDriveVector(vector:TsearchDriveVector):boolean;
Var regs:TTregisters;
    tmp1,tmp2:word;
begin
regs.ax:=$E902;
Move(vector,GlobalReqBuf^,sizeof(TsearchDriveVector));
GetGlobalBufferAddress(regs.ds,regs.dx,tmp1,tmp2);
{ DS:DX real-mode address of GlobalRequestBuffer }
RealModeIntr($21,regs);
result:=0;
SetSearchDriveVector:=True;
end;

Function IsSearchDrive(DriveNumber:byte):boolean;
Var pth:string;
begin
IsSearchDrive:=(getEnvPath(pth)
               and (pos(chr(DriveNumber+ord('A'))+':',pth)>0));
end;


{E905 (shell 3.00+)}
Function MapFakeRootDirectory(DriveNumber:byte; DirPath:string):boolean;
{ Dirpath may include server and volumename }
Var regs:TTregisters;
    tmp1,tmp2:word;
    PName:string;
begin
with regs
 do begin
    ax:=$E905;
    bl:=driveNumber+1;  { FF default, 0=A, 2= B etc. }
    GetGlobalBufferAddress(ds,dx,tmp1,tmp2);
    { VLM patch for SERVER/VOL: and VOL: type paths }
    if (DirPath[0]>#2) and (pos(':',DirPath)>2)
     then GetTrueEntryName(DirPath,PName)
     else PName:=DirPath;
    Pname:=Pname+#0;
    move(PName[1],GlobalReqBuf^,ord(PName[1]));
    { DS:DX real-mode address of GlobalRequestBuffer holding new path }
    RealModeIntr($21,regs);
    if (flags and 1 {carry})>0
     then result:=al
     else result:=0;
    end;
MapFakeRootDirectory:=(result=0);
{ $00 Successful $03 Invalid path $0F Invalid Drive $11 Not same device }
end;

{E906 (shell 3.00+)}
Function DeleteFakeRootDirectory(DriveNumber:byte):boolean;
Var regs:TTregisters;
begin
with regs
 do begin
    ax:=$E906;
    bl:=DriveNumber+1;
    RealModeIntr($21,regs);
    result:=0;
    end;
DeleteFakeRootDirectory:=(result=0);
end;

{E907 (shell 3.00+)}
Function GetRelativeDriveDepth(DriveNumber:byte; Var depth:byte):boolean;
Var regs:TTregisters;
begin
with regs
 do begin
    ax:=$E907;
    bl:=DriveNumber+1;
    RealModeIntr($21,regs);
    depth:=al;
    if al<$FF
     then result:=0
     else result:=$FF; { no fake root assigned }
    end;
GetRelativeDriveDepth:=(result=0);
{ 00 Succesful $FF No fake root assigned }
end;

{secondary}
Function DeleteDriveMapping(DriveNumber:Byte):boolean;
Var dirHandle,status:byte;
    pth:string;
    ch:char;
    p:byte;
    DDepth,Dflag:byte;
begin
{ if searchdrive, remove drive from searchtable and PATH environment string }
IF GetEnvPath(pth)
 then begin
      if pth[ord(pth[0])]<>';'
       then pth:=pth+';';
      p:=pos(chr(DriveNumber+ord('A'))+':',pth);
      if p>0
       then begin { it is a searchdrive, remove from path }
            Repeat
             ch:=pth[p];
             delete(pth,p,1);
            UNTIL ch=';';
            SetEnvPath(pth); { also creates a new searchdriveVector }
            end;
      end;
IF (result=0) and GetDirectoryHandle(DriveNumber,dirHandle,status)
 then begin
      IF GetRelativeDriveDepth(DriveNumber,DDepth) { is it a fake root ? }
       then DeleteFakeRootDirectory(DriveNumber);
      GetDriveFlag(DriveNumber,Dflag);
      SetDriveFlag(DriveNumber,(Dflag and $F0) or DRIVE_UNUSED);
      SetDriveHandle(DriveNumber,0);
      SetDriveConnectionId(DriveNumber,0);
      DeallocateDirHandle(dirHandle);
      end;
DeleteDriveMapping:=(result=0);
end;


{secondary }
FUNCTION MapPermanentDrive(DriveNumber:byte; DirectoryPath:string;
                           Root:boolean):boolean;
var pth        : string;
    DriveHandle: Byte;
begin
IF GetTrueEntryName(DirectoryPath,pth)
 then begin
      while pth[ord(pth[0])] IN ['\','.','*','?']
       do dec(pth[0]);
      if pth[1]<>'\'
       then result:=$104 { attempt to map network drive to local drive }
       else begin
            If GetDriveHandle(DriveNumber,DriveHandle) and (DriveHandle<>0)
             then DeleteDriveMapping(DriveNumber);

            IF MapFakeRootDirectory(DriveNumber,pth)
             then begin
                  if (not root)
                   then DeleteFakeRootDirectory(DriveNumber);
                    { does not delete the mapping itself,
                      only the fake root. }
                  end;
            end;
      end
 else result:=$101; { direcory not locatable }
MapPermanentDrive:=(result=0);
end;

{secondary}
FUNCTION MapDrive(DriveNumber:Byte; DirectoryPath:string;
                  Root, Permanent:boolean):boolean;
var rights      : byte;
    newHandle   : byte;
    HandlePth,pth,srvr,vol: string;
    OldConnId,VolConnId:byte;
    p:byte;
    VolNbr:byte;
    Dflag:byte;
begin
IF Permanent
 then begin
      MapDrive:=MapPermanentDrive(DriveNumber,DirectoryPath,Root);
      exit;
      end;
{ map temporary drive }
IF GetTrueEntryName(DirectoryPath,pth)
 then begin
      if pth[ord(pth[0])]<>'\'
       then pth:=pth+'\';
      if pth[1]<>'\'
       then result:=$104 { attempt to map network drive to local drive }
       else begin
            delete(pth,1,2);
            p:=pos('\',pth);
            if p=0 then result:=$106;
            srvr:=copy(pth,1,p-1);
            delete(pth,1,p);
            p:=pos('\',pth);
            if p=0 then result:=$105; { volume does not exist }
            vol:=copy(pth,1,p-1);
            delete(pth,1,p);
            IF NOT GetConnectionId(srvr,VolConnId)
             then result:=$106; { server does not exist }
            end;
      end
 else result:=$101; { direcory not locatable }
if (result=0)
 then begin
      while pth[ord(pth[0])] IN ['\','.','*','?']
       do dec(pth[0]);

      { rebuild path: Alloc handle requires VOL:path format }
      HandlePth:=vol+':\'+pth;
      GetPreferredConnectionId(OldConnId);
      SetPreferredConnectionId(VolConnId);

     { IF Permanent
       then AllocPermanentDirHandle(DriveNumber,0,HandlePth,
                                    newHandle,rights)
       else}
      AllocTemporaryDirHandle(DriveNumber,0,HandlePth,
                                    newHandle,rights);
      if (result=0)
       then begin
            GetDriveFlag(DriveNumber,Dflag);
            {If Permanent
             then SetDriveFlag(DriveNumber,(Dflag and $F0) or DRIVE_PERMANENT)
             else}
            SetDriveFlag(DriveNumber,(Dflag and $F0) or DRIVE_TEMPORARY);
            SetDriveHandle(DriveNumber,newHandle);
            SetDriveConnectionId(DriveNumber,VolConnId);
            IF root
             then MapFakeRootDirectory(DriveNumber,'\\'+srvr+'\'+vol+'\'+pth);
            end;
      SetPreferredConnectionId(OldConnId);
      end;
MapDrive:=(result=0);
end;



Function MapSearchDrive(DriveNumber:byte; DirPath:string;
                        PathPosition:byte;
                        Insert,Root,Permanent:Boolean):boolean;
Var pth:string;
    p,scCount:byte;
    ch:char;
begin
IF MapDrive(DriveNumber,DirPath,Root,Permanent)
 then begin
      GetEnvPath(pth);
      if pth[ord(pth[0])]<>';'
       then pth:=pth+';';
      scCount:=1;p:=1;
      while (scCount<PathPosition) and (p<=ord(pth[0]))
       do begin
          if pth[p]=';'
           then inc(scCount);
          inc(p);
          end;
      { pth[p] is first character of PathPositions' entry in path }
      If p<ord(pth[0])
       then begin
            IF (Not Insert)
             then Repeat
                  ch:=pth[p];
                  delete(pth,P,1);
                  UNTIL (ch=';') or (p>=ord(pth[0]));
            pth:=copy(pth,1,p-1)+chr(DriveNumber+ord('A'))
                       +':.;'+copy(pth,p,255);
            end
       else pth:=pth+chr(DriveNumber+ord('A'))+':.;';
      SetEnvPath(pth);
      end;
MapSearchDrive:=(result=0);
end;

{secondary}
Function DeleteConnectionsDriveMappings(ConnId:Byte):Boolean;
Var t,connId2,res:Byte;
begin
res:=$FF;
for t:=0 to 31
 do if GetDriveConnectionId(t,connId2) and (connId2=connId)
     then begin
          DeleteDriveMapping(t);
          if result=0
           then res:=0;
          end;
result:=res;
DeleteConnectionsDriveMappings:=(result=0);
{00 successful  FF No mappings affected OR Invalid connectionId }
end;


{4409 / implemented as a secondary function }
Function IsNetworkDrive(driveNumber:Byte):boolean;
{ isNetworkDrive is set to TRUE if the drive is a) a network drive, and
  b) a legal drive letter was used.                                         }
Var regs:TTRegisters;
begin
With regs
do begin
   AX:=$4409;
   BL:=DriveNumber+1;
   RealModeIntr($21,Regs);
   IsNetworkDrive:=(DX and $1000)<>0
   end;
end;


{--======================-- Entries --===============================--}


{60.. (extended DOS call)}
Function GetTrueEntryName(DirPath:string; Var CanonicalPath:string):boolean;
{ SERVER/VOL:[\]Path   -> \\SERVER\VOL\path
  VOL:[\]Path          -> \\effective_server_name\VOL\path
  D:\                  -> D:\.

{ if a volumename is supplied without a servername, the name of the
  effective server will be returned. }

{ Format of returned string:
  a) D:\path\file.ext or
  b) \\servername\volumename\path\file.ext }

LABEL skip;

Var reply  :array[1..128] of byte;
    regs   :TTregisters;
    pcolon,
    pslash :byte;
    srvr,
    volname:string[47];
    connId :Byte;
begin
{ ----- Pre processing }
if DirPath[0]>#2
 then begin
      if ((DirPath[1]='\') and (DirPath[2]='\'))
       then begin
            CanonicalPath:=DirPath;
            UpString(Canonicalpath);
            goto skip
            end;
      pcolon:=pos(':',DirPath);
      if (pcolon=2) and (DirPath[0]=#3) and (DirPath[3]='\')
       then DirPath:=DirPath+'.';
      { fix known problem of netware: D:\. instead of D:\ }
      if (pcolon=2) and (DirPath[0]=#2)
       then DirPath:=DirPath+'.';
      { fix know problem of -among others- OS/2-dos: D:. instead of D: }
      end;
pcolon:=pos(':',DirPath);
if pcolon>2
 then begin { format must be VOL:[\]path or SERVER/VOL:[\]Path }
      pslash:=pos('/',DirPath);
      if pslash=0
       then pslash:=$FF;
      if (pslash<pcolon)
       then begin { SERVER/VOL: }
            srvr:=copy(DirPath,1,pslash-1);
            delete(DirPath,1,pslash);
            pcolon:=pos(':',DirPath);
            end
       else begin { VOL: }
            GetEffectiveConnectionId(connId);
            GetFileServerName(connId,srvr);
            end;
      volname:=copy(dirPath,1,pcolon-1);
      delete(dirPath,1,pcolon);
      if (dirPath[0]>#0) and (dirPath[1]='\')
        then delete(DirPath,1,1);
      DirPath:='\\'+srvr+'\'+volname+'\'+DirPath;
      end;
if dirPath=''
 then dirPath:='\';
{ ----- actual call }
dirPath:=dirPath+#0;   { zero terminate }
WITH regs
 do begin
    Move(dirPath[1],GlobalReqBuf^,ord(dirPath[0]));
    GetGlobalBufferAddress(ds,si,es,di);
    { DS:SI real mode pointer to GlobalRequestBuffer holding asciiz path ;
      ES:DI real mode pointer to GlbalReplyBuffer }
    ah:=$60;
    RealModeIntr($21,regs);
    Move(GlobalReplyBuf^,reply[1],128);
    if (regs.flags and 1 {carry})>0
     then begin
          result:=ax;
          reply[1]:=0;
          end
     else result:=0;
    end;
ZstrCopy(CanonicalPath,reply[1],128);
{ ----- post-processing -- strip \ and . }
skip: ;
While CanonicalPath[ord(CanonicalPath[0])] in ['\','.']
 do dec(CanonicalPath[0]);
GetTrueEntryName:=(result=0);
{ $00  successful
  $02 Invalid component in directory path OR drive letter only
  $03 Malformed path OR invalid drive letter                    }
end;



{3B.. }
Function ChangeDirectory(DirPath:string):boolean;
{ does not change the default drive }
Var regs:TTregisters;
    tmp1,tmp2:word;
begin
if DirPath[0]>#63
 then result:=$110 { length of path too long }
 else begin
      DirPath:=DirPath+#0;
      with regs
       do begin
          ah:=$3b;
          Move(DirPath[1],GlobalReqBuf^,ord(DirPath[0]));
          GetGlobalBufferAddress(ds,dx,tmp1,tmp2);
          { DS:DX real-mode pointer to GlobalRequestBuffer holding DirPath }
          RealModeIntr($21,regs);
          If (flags and 1 {carry})>0
           then result:=$111 { invalid pathname }
           else result:=0;
          end;
      end;
ChangeDirectory:=(result=0);
end;

{F216/0A [2.15+]}
Function CreateDirectory(DirHandle:Byte; DirPath:string; MaxRightsMask:byte):boolean;
Type Treq=record
          len       :word;
          subFunc   :byte;
          _DirHandle:byte;
          _MRM      :byte;
          _dirPath  :string[255];
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalreqBuf)^
do begin
   subFunc:=$0A;
   _dirHandle:=DirHandle;
   _MRM:=MaxRightsMask;
   _DirPath:=DirPath;
   len:=4+ord(_dirPath[0]);
   F2SystemCall($16,len+2,0,result);
   end;
CreateDirectory:=(result=0)
{ 00 successful   84 No create privileges  98 Volume doesn't exist
  FF directory already exists }
end;


{F216/0B [2.15c+]}
Function DeleteDirectory(DirHandle:Byte; DirPath:string):boolean;
Type Treq=record
          len       :word;
          subFunc   :byte;
          _DirHandle:byte;
          unused    :byte;
          _DirPath  :string[255];
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalreqBuf)^
do begin
   subFunc:=$0B;
   _DirHandle:=DirHandle;
   _DirPath:=DirPath;
   unused:=0;
   len:=4+ord(_DirPath[0]);
   F2SystemCall($16,len+2,0,result);
   end;
DeleteDirectory:=(result=0)
{ 00 successful            8A No delete privileges
  98 Volume doesn't exist  9B Bad directory handle
  9C Invalid path          9F Directory in use
  A0 Directory not empty }
end;


{F217/F4 [3.0+]}
Function ConvertPathToDirEntryId(dirHandle:Byte; dirPath:string;
                             Var VolNbr    :byte;
                             Var dirEntryID:LongInt):boolean;
{ aka ConvertPathToDirEntry }
Type Treq=record
          len       :word;
          subFunc   :byte;
          _dirHandle:byte;
          _DirPath  :string[255];
          end;
     Trep=record
          _volNbr:Byte;
          _EntryId:Longint; {hi-lo}
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
do begin
   subFunc:=$F4;
   _dirHandle:=DirHandle;
   _dirPath:=DirPath;
   UpString(_DirPath);
   If DirHandle=0
    then ConvertPathToVolFormat(_DirPath);
   len:=3+ord(_DirPath[0]);
   F2SystemCall($17,len+2,SizeOf(Trep),result);
   end;
With TPrep(GlobalReplyBuf)^
do begin
   VolNbr    :=_volNbr;
   dirEntryId:=_EntryId;
   end;
ConvertPathToDirEntryId:=(result=0)
{ 00 Successful        9B Bad directory Handle
  9C Invalid Path      C6 No console rights }
end;

{F217/F3 [3.0+]}
Function MapDirEntryIdToPath(VolNbr:byte;DirEntryId:Longint; NStype:byte;
                             Var ExtPath:string):boolean;
{aka MapDirectoryNumberToPath }
{ Returns full path/ with nameSpace information;
  Doesn't return server or volumename. }
Type Treq=record
          len     :word;
          subFunc :byte;
          _VolNbr :byte;
          _EntryId:longint; {hi-lo}
          _NameSp :byte;
          end;
     Trep=record
          _path:array[1..255] of byte; {!! maximum: 512 bytes in path ! }
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Var TempPath:string;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$F3;
    _VolNbr:=VolNbr;
    _EntryId:=DirEntryId;
    _NameSp:=NStype;
    end;
F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result);
if result=0
 then begin
      With TPrep(GlobalReplyBuf)^
       do ZstrCopy(TempPath,_path,255);
      { TempPath according to the 'new' Novell format;
        translate into a 'DOS' style path }
      NovPath2DOSPath(TempPath,ExtPath); { dir\subdir (no server or volume name) }
      end;
MapDirentryIdtoPath:=(result=0)
{ 00 Successful   C6 No console rights  FF ? }
end;


{F216/02}
Function ScanDirectoryInformation(dirHandle:byte; searchDirPath:string;
                            {i/o} Var sequenceNumber:word;
                           {out:} Var dirInfo:Tentry    ):boolean;
{ set sequenceNumber to 0 before the first call.

  If wildcards (* or ?) are included in the searchDirPath:
  Iterate until a $9C error is returned.

  If you don't include a wildcard in the searchDirPath, only use
  this call once. Do not iterate, the same entry will be returned
  eternaly.

  }
Type Treq=record
          len          :word;
          subFunc      :byte;
          _dirHandle   :byte;
          _subDirNumber:word; {hi-lo}
          _dirPath     :string[255]
          end;
     Trep=record
          _subDirName   :array[1..16] of byte;
          _creationDate :word;
          _creationTime :word;
          _ownerObjId   :LongInt; {hi-lo}
          _maxRightsMask:word;
          _SubDirNbr    :word; {hi-lo}
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    subFunc:=$02;
    _dirHandle:=dirHandle;
    _subDirNumber:=swap(sequenceNumber); { force hi-lo}
    _dirPath:=searchDirPath;
    UpString(_dirPath);
    len:=5+ord(searchDirPath[0]);
    F2SystemCall($16,len+2,SizeOf(Trep),result);
    end;
With TPrep(GlobalReplyBuf)^
 do begin
    FillChar(dirInfo,SizeOf(Tentry),0);
    ZstrCopy(dirInfo.EntryName,_SubDirName,16);

    DosTime2NovTime(MakeLong(swap(_CreationDate),swap(_CreationTime)),
                    dirInfo.creationTime);
    dirInfo.ownerId:=Lswap(_ownerObjId);
    dirInfo.RightsMask:=_maxRightsMask;
    sequenceNumber:=swap(_SubDirNbr)+1;
    end;
ScanDirectoryInformation:=(result=0)
{resultcodes: $00 success;  $98 Volume does not exist;
              $9B Bad directory Handle $9C Invalid Path }
end;


{F216/0F [2.0/2.1/3.x]}
Function RenameDirectory( dirHandle:Byte; dirPath, newDirName :String):Boolean;
{ The new directory name must be a regular (legal) directory name,
  max 14 chars long.
  The user must have Parental and Modify rights in the parent directory of
  the directory to be renamed.                                            }
Type Treq=record
          len         :word;
          subFunc     :byte;
          _dirHandle  :Byte;
          _dirNames   :Array[0..255+1+14] of byte; { _dirpath[0] is allowed to be 0 }
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    subFunc:=$0F;
    _dirHandle:=dirHandle;
    Upstring(dirPath);
    UpString(newDirName);
    Move(DirPath[0],_DirNames[0],ord(DirPath[0])+1);
    Move(newDirName[0],_DirNames[1+_DirNames[0]],ord(newDirName[0])+1);
    len:=4+ord(dirPath[0])+ord(newDirName[0]);
    F2SystemCall($16,len+2,0,result);
    end;
RenameDirectory:=(result=0)
{ Possible ResultCodes:
  8B No Rename Privileges; 9B Bad Directory Handle;
  9C Invalid Path; 9E Invalid (new) Dir Name.                                  }
end;


{F216/1F [2.15c+]}
Function GetDirectoryEntry(DirHandle:byte;
                       Var dirEntry:Tentry):boolean;
Type Treq=record
          len:word;
          subFunc:byte;
          _dirHandle:byte;
          end;
     Trep=record
          _Entry       :TintEntry;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$1F;
    _dirHandle:=dirHandle;
    end;
F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result);
With TPrep(GlobalReplyBuf)^
 do begin
    Convert2ExtEntry(_entry,dirEntry);
    end;
GetDirectoryEntry:=(result=0)
{ 00 successful            98 Volume doesn't exist
  9B Bad directory handle  9C Invalid path           }
end;


{B601 [2.0+] }
function SetExtendedFileAttributes(FilePath:String; Attr:Byte) : Boolean;
{ See GetExtFAttr for meaning of Attr the Attribute
  Function result code:
    00h Success;
    FFh File not found;
    FEh Access denied }
Var Novregs:TTRegisters;
    tmp1,tmp2:word;
begin
with NovRegs
do begin
   AX := $B601;
   if FilePath[0]=#255
    then FilePath[255]:=#0
    else FilePath:=FilePath+#0;
   Move(FilePath[1],GlobalReqBuf^,ord(FilePath[0]));
   GetGlobalBufferAddress(ds,dx,tmp1,tmp2);
   { DS:DX real mode pointer to GlobalRequestBuffer holding FilePath }
   CL := Attr;
   RealModeIntr($21,NovRegs);
   IF (Flags AND 1 {carry})>0
    then Result:=AL
    else Result:=$00;
   Result := AL
   end;
SetExtendedFileAttributes:=(Result=0);
end;



{B600 [2.0+]}
function GetExtendedFileAttributes(FilePath:String; var Attributes:Byte) : Boolean;
{ Meaning of Attributes:
  7   6   5   4   3   2   1   0
  |   |   |   |       |   |   |
  |   |   |   |       +---+---+------Search mode
  |   |   |   +----------------------transactional bit  A_TRANSACTIONAL
  |   |   +--------------------------Indexing bit       A_INDEXED
  |   +------------------------------Read Audit bit     A_READ_AUDIT
  +----------------------------------Write Audit bit    A_WRITE_AUDIT
 }
Var NovRegs:TTRegisters;
    tmp1,tmp2:word;
begin
with NovRegs
do begin
   AX := $B600;
   if FilePath[0]=#255
    then FilePath[255]:=#0
    else FilePath:=FilePath+#0; { null terminate string }
   Move(FilePath[1],GlobalReqBuf^,ord(FilePath[0]));
   GetGlobalBufferAddress(ds,dx,tmp1,tmp2);
   { DS:DX real mode pointer to GlobalRequestBuffer hloding FilePath }
   RealModeIntr($21,NovRegs);
   IF (Flags and 1 {carry})>0
    then Result := AL
    else Result:=$00;
   Attributes := CL;
   end;
GetExtendedFileAttributes:=(Result=0);
{ $8C caller lacks privileges
  FEh not permitted to search directory
  FFh file not found }
end;




{F3.. [2.x/3.x]}
Function FileServerFileCopy( sourceFileHandle, destFileHandle:word;
                             sourceFileOffset, destFileOffset:Longint;
                             numberOfBytesToCopy :Longint;
                         VAR numberOfBytesCopied :Longint       ):boolean;
{Note:	both source and destination must be on the same file server
SeeAlso: 3C..,3F..}
Type Treq=record
        _sFH,_dFH      :word;    {lo-hi} {as returned by GetFileHandle.}
        _sFoffs,_dfOffs:Longint; {lo-hi}
        _NbrOfBytes    :Longint; {lo-hi}
        end;
     TPreq=^Treq;
Var regs:TTRegisters;
    tmp1,tmp2:word;
begin
with TPreq(GlobalReqBuf)^
 do begin
    _sFH:=sourceFileHandle;
    _dFH:=destFileHandle;
    _sFoffs:=sourceFileOffset;
    _dFoffs:=destFileOffset;
    _NbrOfBytes:=numberOfBytesToCopy;
    end;
with regs
 do begin
    AH:=$F3;
    GetGlobalBufferAddress(es,di,tmp1,tmp2);
    { ES:DI real mode pointer to GlobalRequestBuffer }
    RealModeIntr($21,regs);
    result:=AL;
    end;
numberOfBytesCopied:=MakeLong(regs.cx,regs.dx); { ? swap those regs for correct byte order ? }
FileServerFileCopy:=(Result=0);
end;

{level-0 function. See GetFileAttributes and SetFileAttributes }
Function DoFileAttributes(subf:byte;FilePath:string;VAR attr:byte):boolean;
Var regs:TTregisters;
    tmp1,tmp2:word;
begin
with regs
do begin
   AH:=$43;
   AL:=subf;
   if subf=$01 then CX:=attr;
   if filePath[0]=#255
    then filePath[255]:=#0
    else filePath:=filePath+#0;
   Move(FilePath[1],GlobalReqBuf^,ord(FilePath[0]));
   GetGlobalBufferAddress(ds,dx,tmp1,tmp2);
   { DS:DX real mode pointer to GlobalRequestBuffer holding FilePath }
   RealModeIntr($21,regs);
   IF ((Flags and 1 {Fcarry})<>0)
    then result:=AL
    else begin
         result:=$00;
         if subf=$00 then attr:=CX
         end;
   end;
DoFileAttributes:=(result=$00);
{ resultcodes: 00 success;        01 invalid function;
               03 path not found; 05 access denied. }
end;

{4300 [1.x/2.x/3.x]}
Function GetFileAttributes(FilePath:string; Var attr:byte):boolean;
{ A_READ_ONLY,A_HIDDEN,A_SYSTEM and A_SHAREABLE only. }
begin
GetFileAttributes:=DoFileAttributes($00,FilePath,attr);
end;

{4301 [1.x/2.x/3.x]}
Function SetFileAttributes(FilePath:string; attr:byte):boolean;
{ A_READ_ONLY,A_HIDDEN,A_SYSTEM and A_SHAREABLE only. }
Var _attr:byte;
begin
_attr:=attr;
SetFileAttributes:=DoFileAttributes($01,FilePath,_attr);
end;



{F217/0F [2.15c+]}
Function ScanFileInformation(DirHandle:Byte; FilePath:string;
                             SearchAttrib:Byte;
                        {i/o} VAR SequenceNbr:Integer;
                        {out} VAR fileInfo:Tentry):Boolean;
{ To be called Iteratatively; initial value for seqNbr=-1 }
{ wildcards in filename allowed.
  Iterate util an error $FF occurs }
Type Treq=record
          len          :word;
          subFunc      :byte;
          _seqNbr      :word; {hi-lo}
          _dirHandle   :byte;
          _searchAttrib:Byte;
          _filePath    :string;
          end;
     Trep=record
          _seqNbr      :word;    {hi-lo}
          _fileName    :array[1..14] of byte;
          _Fattr,
          _ExtFattr    :Byte;
          _Fsize       :LongInt; {hi-lo}
          _Crdate      :word;    {hi-lo}
          _LastAccDate :word;    {hi-lo}
          _LastUpdDate,
          _LastUpdTime :Word;
          _ownerObjId  :Longint; {hi-lo}
          _LastArchDate,
          _lastArchTime:Word;
          _reserved    :array[1..56] of byte;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
do begin
   subFunc:=$0F;
   _seqNbr:=swap(word(SequenceNbr)); { force hi-lo }
   _dirHandle:=dirHandle;
   _searchAttrib:=searchAttrib;
   _filePath:=FilePath;
   len:=6+ord(_filePath[0]);
   F2SystemCall($17,len+2,SizeOf(Trep),result);
   end;
with TPrep(GlobalReplyBuf)^
do begin
   FillChar(fileInfo,sizeOf(fileInfo),#0);
   SequenceNbr:=Integer(swap(_seqNbr)); { force lo-hi }
   ZstrCopy(fileInfo.EntryName,_filename,15);
   fileInfo.Attributes:=(_ExtFattr SHL 8)+_Fattr;
   fileInfo.filesize:=Lswap(_Fsize);     { force lo-hi}
   fileinfo.OwnerID:=Lswap(_ownerObjID); { force lo-hi}
   DosTime2NovTime(MakeLong(swap(_CrDate),0),fileinfo.creationTime);
   DosTime2NovTime(MakeLong(swap(_LastAccDate),0),fileinfo.lastAccessTime);
   DosTime2NovTime(MakeLong(swap(_LastUpdDate),swap(_LastUpdTime))
                   ,fileinfo.ModifyTime);
   DosTime2NovTime(MakeLong(swap(_LastArchDate),swap(_lastArchTime))
                   ,fileinfo.ArchiveTime);
   end;
ScanFileInformation:=(result=0)
{ 89 No search privileges  FF No more matching files }
end;


{F217/10 [2.15c+]}
Function SetFileInformation(DirHandle:Byte; FilePath:string;
                            SearchAttrib:Byte;
                            fileInfo:TEntry):boolean;
Type Treq=record
          len          :word;
          subFunc      :byte;
          _Fattr,
          _ExtFattr    :Byte;
          reserved1    :LongInt; {hi-lo}
          _crDate      :word;    {hi-lo}
          _lastAccDate :word;    {hi-lo}
          _lastUpdTime :Longint;
          _ownerObjId  :Longint; {hi-lo}
          _lastArchTime:Longint;
          reserved2    :array[1..56] of byte;
          _dirHandle   :Byte;
          _searchAttr  :byte;
          _filePath    :string;
          end;
     TPreq=^Treq;
Var DummyDate:Longint;
Begin
WITH TPreq(GlobalReqBuf)^
do begin
   subFunc:=$10;
   _Fattr:=Lo(LowLong(fileInfo.Attributes));
   _ExtFattr:=Hi(LowLong(fileinfo.Attributes));
   _ownerObjId:=Lswap(fileinfo.OwnerId); {force hi-lo}
   _dirHandle:=DirHandle;
   _searchAttr:=SearchAttrib;
   _filePath:=FilePath;
   If Dirhandle=0
    then ConvertPathToVolFormat(_FilePath);
   UpString(_filePath);
   NovTime2DosTime(fileinfo.CreationTime,dummyDate);
   _crDate:=HiLong(dummyDate);
   NovTime2DosTime(fileinfo.LastAccessTime,dummyDate);
   _lastAccDate:=HiLong(dummyDate);
   NovTime2DosTime(fileinfo.ModifyTime,_lastUpdTime);
   NovTime2DosTime(fileinfo.ArchiveTime,_lastArchTime);
   len:=82+ord(_filepath[0]);
   F2SystemCall($17,len+2,0,result);
   end;
SetFileInformation:=(result=0);
{ result codes: 00 Success }
end;


{F244 [2.1x/3.x]}
Function EraseFiles(dirHandle, searchAttrib:Byte; filePath:string ):boolean;
{ marks files for deletion / in DOS parlance: delete file, file remains purgable }
Type Treq=record
          _dirHandle:Byte;
          _Sattr:Byte;
          _filePath:string;
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalReqBuf)^
do begin
   _dirHandle:=dirHandle;
   _Sattr:=searchAttrib;
   _filePath:=filePath;
   F2SystemCall($44,3+ord(_filepath[0]),0,result);
   end;
EraseFiles:=(result=0);
{ resultcodes: 00 Success; 98h Volume doesn't exist; 9Bh bad directory handle;
              9Ch invalid path; FFh no files found error. }
end;

{F216/1B [3.0+]}
Function ScanSalvagableFiles(DirHandle:Byte;
                      {i/o}  Var EntryId:Longint;
                      {out}  Var Entry:Tentry   ):boolean;
{ Iterate (with entryId set to -1 at first) until an error $FF occurs }
Type Treq=record
          len       :word;
          subFunc   :byte;
          _DirHandle:Byte;
          _EntryId  :Longint; {low_word-hi_word & each word lo-hi }
          end;
     Trep=record
          _EntryId     :Longint;
          _Entry       :TintEntry;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$1B;
    _DirHandle:=DirHandle;
    _EntryId:=EntryId;
    end;
F2SystemCall($16,SizeOf(Treq),Sizeof(Trep),result);
With TPrep(GlobalReplyBuf)^
 do begin
    EntryId:=_EntryId; {return next EntryId for iteration}
                        {low_word-hi_word & each word lo-hi }
    Convert2ExtEntry(_Entry,Entry);
    end;
ScanSalvagableFiles:=(result=0)
{ 98 Volume does not exist  FF No more erased files }
end;

{F216/1D  [3.0+]}
Function PurgeSalvagableFile(DirHandle:Byte;
                             EntryId:Longint; FileName:string):boolean;
{ either supply an entryId and an empty filename,
  or supply an entryId of -1 and a filename. Note that the filename
  may not be unique: there may be more than one old deleted versions
  of a filename. }
Type Treq=record
          len       :word;
          subFunc   :byte;
          _DirHandle:Byte;
          _EntryId  :Longint;   {low_word-hi_word & each word lo-hi }
          _Name     :string[255];
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    subFunc:=$1D;
    _DirHandle:=DirHandle;
    _EntryId:=EntryId;
    _Name:=FileName;
    UpString(_name);
    len:=7+ord(_Name[0]);
    F2SystemCall($16,len+2,0,result);
    end;
PurgeSalvagableFile:=(result=0)
end;

{F216/1C [3.0+] }
Function RecoverSalvagableFile(dirHandle:Byte; EntryId:Longint;
                               OldName,NewName:string):boolean;
{ entryId may be set to -1
  OldName is the name of the file before it was deleted.
  NewName is the name to be assigned to the recovered file }
Type Treq=record
          len           :word;
          subFunc       :byte;
          _DirHandle    :Byte;
          _EntryId      :Longint;  {low_word-hi_word & each word lo-hi }
          _OldAndNewName:string[255];
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    subFunc:=$1C;
    _DirHandle:=DirHandle;
    _EntryId:=EntryId;
    UpString(OldName);
    UpString(NewName);
    _OldAndNewName:=OldName;
    move(NewName[0],_OldAndNewName[ord(oldname[0])+1],ord(NewName[0])+1);
    len:=8+ord(oldName[0])+ord(NewName[0]);
    F2SystemCall($16,len+2,0,result);
    end;
RecoverSalvagableFile:=(result=0)
{ 98 Volume does not exist  FF No more erased files }
end;


{F216/24 [3.0+]}
Function SetDirRestriction(DirHandle:Byte; DiskSpaceLimit:Longint):boolean;
{ limit expressed in Blocks. set limit to 0 to lift limit.
  use a negative number if limit should be equal to 0 }
Type Treq=record
          len       :word;
          subFunc   :byte;
          _DirHandle:Byte;
          _Limit    :Longint;
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$24;
    _DirHandle:=DirHandle;
    _Limit:=DiskSpaceLimit;
    end;
F2SystemCall($16,SizeOf(Treq),0,result);
SetDirRestriction:=(result=0)
end;


{F216/23 [3.0+]}
Function ScanDirRestrictions(DirHandle:Byte;
                           Var NumberOfEntries:Byte;
                           Var RestrInfo:TdirRestrList):boolean;
Type Treq=record
          len:word;
          subFunc:byte;
          _DirHandle:Byte;
          end;
     Trep=record
          _Entries:Byte;
          _Info:TdirRestrList;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$23;
    _DirHandle:=DirHandle;
    end;
F2SystemCall($16,SizeOf(Treq),Sizeof(Trep),result);
With TPrep(GlobalReplyBuf)^
 do begin
    NumberOfEntries:=_Entries;
    RestrInfo:=_Info;
    end;
ScanDirRestrictions:=(result=0)
end;


Procedure FixEntryNameFormat(Var s:string);
Var res:string;
    p:byte;
begin
res:='';
for p:=1 to ord(s[0])
 do begin
    if s[p]='?'
     then res:=res+#$FF+#$BF
     else if s[p]='*'
           then res:=res+#$FF+'*'
           else res:=res+s[p]
    end;
s:=res;
end;


{F216/1E [2.15c+]}
Function ScanDirectoryEntry(DirHandle:Byte; EntryName:string; SearchFlags:Longint;
                      {i/o} Var EntryId:Longint;
                      {out} Var Entry:Tentry    ):boolean;
Type Treq=record
          len         :word;
          subFunc     :byte;
          _DirHandle  :Byte;
          _SearchFlags:Byte;    { standard: $16 for dirs / $06 for files }
          _SeqNbr     :Longint; { lo-hi , set to -1 initially }
          _EntryName  :string;
          end;

     Trep=record { len = 84h = 132 dec. }
          _EntryID      :Longint;    { lo-hi }
          _Entry        :TintEntry;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    subFunc:=$1E;
    _DirHandle:=DirHandle;
    _SearchFlags:=SearchFlags;
    _SeqNbr:=EntryId;
    _EntryName:=EntryName;UpString(_EntryName);
    FixEntryNameFormat(_EntryName);
    len:=8+ord(_EntryName[0]);
    F2SystemCall($16,len+2,Sizeof(Trep),result);
    end;
With TPrep(GlobalReplyBuf)^
 do begin
    EntryId:=_EntryId; {return next EntryId for iteration}
    Convert2ExtEntry(_Entry,entry);
    end;
ScanDirectoryEntry:=(result=0)
end;

{F216/25 [2.15c+] }
Function SetEntry(DirHandle:Byte;EntryId:Longint;SearchFlags:Byte;
                  ModFlags:Longint; Entry:Tentry                ):boolean;
Type Treq=record
          len       :word;
          subFunc   :byte;
          _dirHandle:Byte;
          _SFlags   :Byte;
          _EntryId  :Longint; {lo-hi}
          _ModFlags :Longint; {lo-hi}
          _Entry    :TintEntry;
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$25;
    _dirHandle:=DirHandle;
    _EntryId:=EntryId;
    _ModFlags:=ModFlags;
    Convert2IntEntry(Entry,_Entry);
    end;
F2SystemCall($16,SizeOf(Treq),0,result);
SetEntry:=(result=0)
end;

{------------------ Secondary Functions ----------------------------}

Function IsFileShareable(Path : String):boolean;

var F: File;
    FAttr : Word;

begin
 { Assign(F, Path);
  GetFAttr(F, FAttr);
  result:=DOSerror; }
  IsFileShareable:=(result=0) and ((FAttr and $80)>0)
end;

function FlagFileShareable(Path : String) : Boolean;
{ when the file could NOT be made shareable, false is returned as the
  function result, a doserror# is returned as the result code. }
var  F : File;
     Attr : Word;
     ErrCode : word;
     Share            : Boolean;
begin
if NOT IsFileShareable(Path) { Share: is it sharable? }
 then begin
      Assign(F,Path);
      {SetFAttr(F,Attr or A_SHAREABLE);  OR existing atrib. with SHARE bit }
      {Result := DOSError;}
      end;
FlagFileShareable := (Result=0);
end;


Function GetFileHandle(Var f):word;
begin
{GetFileHandle:=filerec(f).handle;}
end;

{------===================-- Trustee/Max. Rights masks --=================--}


{F216/27  [3.0+]}
Function SetTrustee(DirHandle:Byte;DirPath:string;
                    TrusteeObjectID:Longint;
                    RightsMask:Word               ):boolean;
Type Treq=record
          len       :word;
          subFunc   :byte;
          _DirHandle:byte;
          _ObjId    :Longint;  { hi-lo }
          _Rights   :Word;     { lo-hi }
          _DirPath  :string;
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    subFunc:=$27;
    _DirHandle:=DirHandle;
    if DirHandle=0
     then ConvertPathToVolFormat(DirPath);
    _DirPath:=DirPath;UpString(_DirPath);
    _ObjId:=Lswap(TrusteeObjectId);
    _Rights:=RightsMask;
    len:=9+ord(_DirPath[0]);
    F2SystemCall($16,len+2,0,result);
    end;
SetTrustee:=(result=0)
{ Possible resultcodes: 8C No modify privileges;
  98 Volume doesn't exist; 9B Bad directory handle
  9C Invalid path; FC No such bindery object }
end;


{F216/2B [3.0+]}
Function DeleteTrustee(DirHandle:Byte;DirPath:String;
                       TrusteeObjectId:Longint):boolean;
{ If DirHandle equals 0, DirPath should be according to the
  VOL:\path format. All other path formats will result in
  an resultcode of 98h (No such volume) }
Type Treq=record
          len       :word;
          subFunc   :byte;
          _DirHandle:byte;
          _ObjId    :Longint;  { hi-lo }
          _Unused   :Byte;
          _DirPath  :string;
          end;
     TPreq=^Treq;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    subFunc:=$2B;
    _DirHandle:=DirHandle;
    if DirHandle=0
     then ConvertPathToVolFormat(DirPath);
    _DirPath:=DirPath;UpString(_DirPath);
    _ObjId:=Lswap(TrusteeObjectId);
    _Unused:=0;
    len:=8+ord(_DirPath[0]);
    F2SystemCall($16,len+2,0,result);
    end;
DeleteTrustee:=(result=0);
{ Possible resultcodes: 98 Volume doesn't exist
  9B Bad directory handle; 9C Invalid path
  FE no such trustee  }
end;


{F216/2A [3.0+]}
function GetEffectiveRights(DirHandle:Byte;DirPath:String;
                        var Rights:Word) : Boolean;
{ returns the requesting workstation's effective directory rights }
Type Treq=record
          Len         : word;
          SubF        : Byte;
          _DirHandle  : Byte;
          _DirName    : String;
          end;
     TRep=record
          _RightsMask : Word;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
begin
with TPreq(GlobalReqBuf)^
 do begin
    SubF        := $2A;
    _DirHandle  := DirHandle;
    if DirHandle=0
     then ConvertPathToVolFormat(DirPath);
    _DirName    := DirPath;UpString(_DirName);
    Len         := 3+ord(DirPath[0]);
    F2SystemCall($16,len+2,SizeOf(Trep),result);
    end;
with TPrep(GlobalReplyBuf)^
 do Rights:=_RightsMask;
GetEffectiveRights:=(Result=0);
{ return byte
  00h  - Success
  98h  - Volume Does Not Exist
  9Bh  - Bad Directory Handle  }
end;


{F216/04 [2.15c+]}
Function ModifyMaximumRightsMask(DirHandle:Byte;DirPath:string;
                                 RevokeRightsMask,GrantRightsMask:Word):boolean;
Type Treq=record
          len:word;
          subFunc:byte;
          _DirHandle:Byte;
          _GrantRM,
          _RevokeRM:Byte;
          _DirPath:String;
          end;
     Trep=record
          _EffectiveRightsMask:Byte;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=5+ord(DirPath[0]);
    subFunc:=$04;
    _DirHandle:=DirHandle;
    if DirHandle=0
     then ConvertPathToVolFormat(DirPath);
    _GrantRM:=MapV3RightsToV2(GrantRightsMask);
    _RevokeRM:=MapV3RightsToV2(RevokeRightsMask);
    _DirPath:=DirPath;
    F2SystemCall($16,len+2,Sizeof(Trep),result);
    end;
{With TPrep(GlobalReplyBuf)^
 do begin
    --- nothing is done with the returned value---
    end;}
ModifyMaximumRightsMask:=(result=0)
{ result codes: 8C No modify privileges; 98 Volume dosn't exist;
                9C Invalid path }
end;



{F217/47 [2.15c+]}
Function ScanBinderyObjectTrusteePaths(TrusteeObjectId:Longint;
                                       VolumeNumber:Byte;
                             {i/o} Var SequenceNumber:word;
                             {out} Var AccessMask:Word;
                                   Var Path:string        ):boolean;
{ You must be supervisor (-equivalent) or the TrusteeObject itself
  to use this function.
  Initially, sequencenumber should be set to 0. }
Type Treq=record
          len    :word;
          subFunc:byte;
          _VolNbr:Byte;
          _SeqNbr:word; {hi-lo}
          _ObjId :Longint; {hi-lo}
          end;
     Trep=record
          _NextSeqNbr:Word; {hi-lo}
          _ObjId     :Longint; {hi-lo}
          _AccMask   :byte;
          _Path      :string;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$47;
    _VolNbr:=VolumeNumber;
    _SeqNbr:=swap(SequenceNumber);
    _ObjId:=Lswap(TrusteeObjectId);
    end;
F2SystemCall($17,SizeOf(Treq),Sizeof(Trep),result);
With TPrep(GlobalReplyBuf)^
 do begin
    SequenceNumber:=Lswap(_NextSeqNbr);
    Accessmask:=_AccMask; {MapV2RightsToV3(_accMask);}
    Path:=_Path;
    end;
ScanBinderyObjectTrusteePaths:=(result=0)
{ resultcodes:
  $96 Server out of memory; $F0 Wildcard not allowed;
  $F1 Invalid bindery security; $FC No such object;
  $FE Server bindery locked; $FF Bindery failure }
end;

{F216/26 [3.0+]}
Function ScanEntryForTrustees(DirHandle:Byte;DirPath:String;
                    {i/o} Var SequenceNumber:Byte;
                    {out} Var TrusteeInfo: TtrusteeInformation):boolean;
{ Set SequenceNumber to 0 initially,
  iterate until error $9C (no more trustees) is returned }
{ see GETTR in the XFILE archive for an example }
Type Treq=record
          len:word;
          subFunc:byte;
          _DirHandle:Byte;
          _SeqNbr:Byte;
          _DirPath:String;
          end;
     Trep=record
          _Info:TtrusteeInformation;
          end;
     TPreq=^Treq;
     TPrep=^Trep;
Var t:Byte;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=4+ord(DirPath[0]);
    subFunc:=$26;
    _DirHandle:=DirHandle;
    if DirHandle=0
     then ConvertPathToVolFormat(DirPath);
    _SeqNbr:=SequenceNumber;
    _DirPath:=DirPath;UpString(_DirPath);
    F2SystemCall($16,len+2,Sizeof(Trep),result);
    end;
With TPrep(GlobalReplyBuf)^
 do begin
    inc(SequenceNumber);
    TrusteeInfo.NumberOfTrustees:=_Info.NumberOfTrustees;
    for t:=1 to 20
     do begin
        TrusteeInfo.TrusteeId[t]:=Lswap(_Info.TrusteeId[t]);
        TrusteeInfo.TrusteeRights[t]:=_Info.TrusteeRights[t];
        end;
    end;
ScanEntryForTrustees:=(result=0)
{ resultcodes:
  $9C No more trustees }
end;




{F2  [2.15c+]
Function (   ):boolean;
Type Treq=record
          len:word;
          subFunc:byte;

          end;
     Trep=record

          end;
     TPreq=^Treq;
     TPrep=^Trep;
Begin
WITH TPreq(GlobalReqBuf)^
 do begin
    len:=SizeOf(Treq)-2;
    subFunc:=$

    end;
F2SystemCall($ ,SizeOf(Treq),Sizeof(Trep),result);
With TPrep(GlobalReplyBuf)^
 do begin

    end;
 :=(result=0)
end; }

end.