Jelajahi Sumber

* first implementation of MorphOS DOS unit, based on Amiga version

Károly Balogh 21 tahun lalu
induk
melakukan
00d8d57c55
1 mengubah file dengan 1379 tambahan dan 0 penghapusan
  1. 1379 0
      rtl/morphos/dos.pp

+ 1379 - 0
rtl/morphos/dos.pp

@@ -0,0 +1,1379 @@
+{
+    $Id$
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2004 by Karoly Balogh for Genesi Sarl
+
+    Heavily based on the Amiga/m68k RTL by Nils Sjoholm and
+    Carl Eric Codere
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+
+unit Dos;
+
+{--------------------------------------------------------------------}
+{ LEFT TO DO:                                                        }
+{--------------------------------------------------------------------}
+{ o DiskFree / Disksize don't work as expected                       }
+{ o Implement EnvCount,EnvStr                                        }
+{ o FindFirst should only work with correct attributes               }
+{--------------------------------------------------------------------}
+
+
+Interface
+
+Const
+  {Bitmasks for CPU Flags}
+  fcarry     = $0001;
+  fparity    = $0004;
+  fauxiliary = $0010;
+  fzero      = $0040;
+  fsign      = $0080;
+  foverflow  = $0800;
+
+  {Bitmasks for file attribute}
+  readonly  = $01;
+  hidden    = $02;
+  sysfile   = $04;
+  volumeid  = $08;
+  directory = $10;
+  archive   = $20;
+  anyfile   = $3F;
+
+  {File Status}
+  fmclosed = $D7B0;
+  fminput  = $D7B1;
+  fmoutput = $D7B2;
+  fminout  = $D7B3;
+
+
+Type
+  ComStr  = String[255];  { size increased to be more compatible with Unix}
+  PathStr = String[255];  { size increased to be more compatible with Unix}
+  DirStr  = String[255];  { size increased to be more compatible with Unix}
+  NameStr = String[255];  { size increased to be more compatible with Unix}
+  ExtStr  = String[255];  { size increased to be more compatible with Unix}
+
+
+
+{
+  filerec.inc contains the definition of the filerec.
+  textrec.inc contains the definition of the textrec.
+  It is in a separate file to make it available in other units without
+  having to use the DOS unit for it.
+}
+{$i filerec.inc}
+{$i textrec.inc}
+
+
+Type
+
+  SearchRec = Packed Record
+    { watch out this is correctly aligned for all processors }
+    { don't modify.                                          }
+    { Replacement for Fill }
+{0} AnchorPtr : Pointer;    { Pointer to the Anchorpath structure }
+{4} Fill: Array[1..15] of Byte; {future use}
+    {End of replacement for fill}
+    Attr : BYTE;        {attribute of found file}
+    Time : LongInt;     {last modify date of found file}
+    Size : LongInt;     {file size of found file}
+    Name : String[255]; {name of found file}
+  End;
+
+
+  DateTime = packed record
+    Year: Word;
+    Month: Word;
+    Day: Word;
+    Hour: Word;
+    Min: Word;
+    Sec: word;
+  End;
+
+  { Some ugly x86 registers... }
+  registers = packed record
+    case i : integer of
+     0 : (ax,f1,bx,f2,cx,f3,dx,f4,bp,f5,si,f51,di,f6,ds,f7,es,f8,flags,fs,gs : word);
+     1 : (al,ah,f9,f10,bl,bh,f11,f12,cl,ch,f13,f14,dl,dh : byte);
+     2 : (eax,  ebx,  ecx,  edx,  ebp,  esi,  edi : longint);
+    end;
+
+
+Var
+  DosError : integer;
+
+{Interrupt}
+Procedure Intr(intno: byte; var regs: registers);
+Procedure MSDos(var regs: registers);
+
+{Info/Date/Time}
+Function  DosVersion: Word;
+Procedure GetDate(var year, month, mday, wday: word);
+Procedure GetTime(var hour, minute, second, sec100: word);
+procedure SetDate(year,month,day: word);
+Procedure SetTime(hour,minute,second,sec100: word);
+Procedure UnpackTime(p: longint; var t: datetime);
+Procedure PackTime(var t: datetime; var p: longint);
+
+{Exec}
+Procedure Exec(const path: pathstr; const comline: comstr);
+Function  DosExitCode: word;
+
+{Disk}
+Function  DiskFree(drive: byte) : longint;
+Function  DiskSize(drive: byte) : longint;
+Procedure FindFirst(path: pathstr; attr: word; var f: searchRec);
+Procedure FindNext(var f: searchRec);
+Procedure FindClose(Var f: SearchRec);
+
+{File}
+Procedure GetFAttr(var f; var attr: word);
+Procedure GetFTime(var f; var time: longint);
+Function  FSearch(path: pathstr; dirlist: string): pathstr;
+Function  FExpand(const path: pathstr): pathstr;
+Procedure FSplit(path: pathstr; var dir: dirstr; var name: namestr; var ext: extstr);
+
+{Environment}
+Function  EnvCount: longint;
+Function  EnvStr(index: integer): string;
+Function  GetEnv(envvar: string): string;
+
+{Misc}
+Procedure SetFAttr(var f; attr: word);
+Procedure SetFTime(var f; time: longint);
+Procedure GetCBreak(var breakvalue: boolean);
+Procedure SetCBreak(breakvalue: boolean);
+Procedure GetVerify(var verify: boolean);
+Procedure SetVerify(verify: boolean);
+
+{Do Nothing Functions}
+Procedure SwapVectors;
+Procedure GetIntVec(intno: byte; var vector: pointer);
+Procedure SetIntVec(intno: byte; vector: pointer);
+Procedure Keep(exitcode: word);
+
+implementation
+
+const
+  DaysPerMonth :  Array[1..12] of ShortInt =
+(031,028,031,030,031,030,031,031,030,031,030,031);
+  DaysPerYear  :  Array[1..12] of Integer  =
+(031,059,090,120,151,181,212,243,273,304,334,365);
+  DaysPerLeapYear :    Array[1..12] of Integer  =
+(031,060,091,121,152,182,213,244,274,305,335,366);
+  SecsPerYear      : LongInt  = 31536000;
+  SecsPerLeapYear  : LongInt  = 31622400;
+  SecsPerDay       : LongInt  = 86400;
+  SecsPerHour      : Integer  = 3600;
+  SecsPerMinute    : ShortInt = 60;
+  TICKSPERSECOND    = 50;
+
+type
+    BPTR     = Longint;
+    BSTR     = Longint;
+
+const
+    LDF_READ   = 1;
+    LDF_DEVICES = 4;
+
+    ERROR_NO_MORE_ENTRIES            = 232;
+    FIBF_SCRIPT         = 64;  { program is a script              }
+    FIBF_PURE           = 32;  { program is reentrant             }
+    FIBF_ARCHIVE        = 16;  { cleared whenever file is changed }
+    FIBF_READ           = 8;   { ignoed by old filesystem         }
+    FIBF_WRITE          = 4;   { ignored by old filesystem        }
+    FIBF_EXECUTE        = 2;   { ignored by system, used by shell }
+    FIBF_DELETE         = 1;   { prevent file from being deleted  }
+
+    SHARED_LOCK         = -2;
+
+{******************************************************************************
+                           --- Internal routines ---
+******************************************************************************}
+
+function Lock(const name : string;
+              accessmode : Longint) : BPTR;
+var
+ buffer: Array[0..255] of char;
+Begin
+  move(name[1],buffer,length(name));
+  buffer[length(name)]:=#0;
+  lock:=dos_Lock(buffer,accessmode);
+end;
+
+FUNCTION BADDR(bval : BPTR): POINTER;
+BEGIN
+    BADDR := POINTER( bval shl 2);
+END;
+
+
+Procedure AmigaToDt(SecsPast: LongInt; Var Dt: DateTime);
+var
+  cd : pClockData;
+Begin
+  New(cd);
+  util_Amiga2Date(SecsPast,cd);
+  Dt.sec   := cd^.sec;
+  Dt.min   := cd^.min;
+  Dt.hour  := cd^.hour;
+  Dt.day   := cd^.mday;
+  Dt.month := cd^.month;
+  Dt.year  := cd^.year;
+  Dispose(cd);
+End;
+
+Function DtToAmiga(DT: DateTime): LongInt;
+var
+  cd : pClockData;
+  temp : Longint;
+Begin
+  New(cd);
+  cd^.sec   := Dt.sec;
+  cd^.min   := Dt.min;
+  cd^.hour  := Dt.hour;
+  cd^.mday  := Dt.day;
+  cd^.month := Dt.month;
+  cd^.year  := Dt.year;
+  temp := util_Date2Amiga(cd);
+  Dispose(cd);
+  DtToAmiga := temp;
+end;
+
+function IsLeapYear(Source : Word) : Boolean;
+Begin
+{$WARNING FIX ME!!! Leap year calculation is "somewhat" buggy.}
+  If (Source Mod 4 = 0) Then
+    IsLeapYear := True
+  Else
+    IsLeapYear := False;
+End;
+
+Procedure Amiga2DateStamp(Date : LongInt; Var TotalDays,Minutes,Ticks: longint);
+{ Converts a value in seconds past 1978 to a value in AMIGA DateStamp format }
+{ Taken from SWAG and modified to work with the Amiga format - CEC           }
+Var
+  LocalDate : LongInt; Done : Boolean; TotDays : Integer;
+  Y: Word;
+  H: Word;
+  Min: Word;
+  S : Word;
+Begin
+  Y   := 1978; H := 0; Min := 0; S := 0;
+  TotalDays := 0;
+  Minutes := 0;
+  Ticks := 0;
+  LocalDate := Date;
+  Done := False;
+  While Not Done Do
+  Begin
+    If LocalDate >= SecsPerYear Then
+    Begin
+      Inc(Y,1);
+      Dec(LocalDate,SecsPerYear);
+      Inc(TotalDays,DaysPerYear[12]);
+    End
+    Else
+      Done := True;
+    If (IsLeapYear(Y+1)) And (LocalDate >= SecsPerLeapYear) And
+       (Not Done) Then
+    Begin
+      Inc(Y,1);
+      Dec(LocalDate,SecsPerLeapYear);
+      Inc(TotalDays,DaysPerLeapYear[12]);
+    End;
+  End; { END WHILE }
+  Done := False;
+  TotDays := LocalDate Div SecsPerDay;
+  { Total number of days }
+  TotalDays := TotalDays + TotDays;
+    Dec(LocalDate,TotDays*SecsPerDay);
+  { Absolute hours since start of day }
+  H := LocalDate Div SecsPerHour;
+  { Convert to minutes }
+  Minutes := H*60;
+    Dec(LocalDate,(H * SecsPerHour));
+  { Find the remaining minutes to add }
+  Min := LocalDate Div SecsPerMinute;
+    Dec(LocalDate,(Min * SecsPerMinute));
+  Minutes:=Minutes+Min;
+  { Find the number of seconds and convert to ticks }
+  S := LocalDate;
+  Ticks:=TICKSPERSECOND*S;
+End;
+
+
+function SetProtection(const name: string; mask:longint): Boolean;
+var
+  buffer : array[0..255] of Char;
+begin
+  move(name[1],buffer,length(name));
+  buffer[length(name)]:=#0;
+  SetProtection:=dos_SetProtection(buffer,mask);
+end;
+
+function SetFileDate(name: string; p : PDateStamp): Boolean;
+var buffer : array[0..255] of Char;
+begin
+  move(name[1],buffer,length(name));
+  buffer[length(name)]:=#0;
+  SetFileDate:=dos_SetFileDate(buffer,p);
+end;
+
+
+{******************************************************************************
+                           --- Dos Interrupt ---
+******************************************************************************}
+
+Procedure Intr (intno: byte; var regs: registers);
+  Begin
+  { Does not apply to MorphOS - not implemented }
+  End;
+
+
+Procedure SwapVectors;
+  Begin
+  { Does not apply to MorphOS - Do Nothing }
+  End;
+
+
+Procedure msdos(var regs : registers);
+  Begin
+  { ! Not implemented in MorphOS ! }
+  End;
+
+
+Procedure getintvec(intno : byte;var vector : pointer);
+  Begin
+  { ! Not implemented in MorphOS ! }
+  End;
+
+
+Procedure setintvec(intno : byte;vector : pointer);
+  Begin
+  { ! Not implemented in MorphOS ! }
+  End;
+
+{******************************************************************************
+                        --- Info / Date / Time ---
+******************************************************************************}
+
+function DosVersion: Word;
+var p: PLibrary;
+begin
+  p:=PLibrary(MOS_DOSBase);
+  DosVersion:= p^.lib_Version or (p^.lib_Revision shl 8);
+end;
+
+{ Here are a lot of stuff just for setdate and settime }
+
+Const
+
+{ unit defintions }
+    UNIT_MICROHZ        = 0;
+    UNIT_VBLANK         = 1;
+
+    TIMERNAME : PChar   = 'timer.device';
+
+Type
+
+
+    ptimeval = ^ttimeval;
+    ttimeval = record
+        tv_secs         : longint;
+        tv_micro        : longint;
+    end;
+
+    ptimerequest = ^ttimerequest;
+    ttimerequest = record
+        tr_node         : tIORequest;
+        tr_time         : ttimeval;
+    end;
+
+Const
+
+{ IO_COMMAND to use for adding a timer }
+    CMD_NONSTD  = 9;
+    TR_ADDREQUEST       = CMD_NONSTD;
+    TR_GETSYSTIME       = CMD_NONSTD + 1;
+    TR_SETSYSTIME       = CMD_NONSTD + 2;
+    MEMF_PUBLIC   = %000000000000000000000001;
+    MEMF_CLEAR    = %000000010000000000000000;
+
+{  To use any of the routines below, TimerBase must be set to point
+   to the timer.device, either by calling CreateTimer or by pulling
+   the device pointer from a valid TimeRequest, i.e.
+
+        TimerBase := TimeRequest.io_Device;
+
+    _after_ you have called OpenDevice on the timer.
+}
+
+var
+    TimerBase   : Pointer;
+
+
+procedure NewList (list: pList);
+begin
+    with list^ do
+    begin
+        lh_Head     := pNode(@lh_Tail);
+        lh_Tail     := NIL;
+        lh_TailPred := pNode(@lh_Head)
+    end
+end;
+
+function CreateExtIO (port: pMsgPort; size: Longint): pIORequest;
+var
+   IOReq: pIORequest;
+begin
+    IOReq := NIL;
+    if port <> NIL then
+    begin
+        IOReq := exec_AllocMem(size, MEMF_CLEAR or MEMF_PUBLIC);
+        if IOReq <> NIL then
+        begin
+            IOReq^.io_Message.mn_Node.ln_Type   := 7;
+            IOReq^.io_Message.mn_Length    := size;
+            IOReq^.io_Message.mn_ReplyPort := port;
+        end;
+    end;
+    CreateExtIO := IOReq;
+end;
+
+procedure DeleteExtIO (ioReq: pIORequest);
+begin
+    if ioReq <> NIL then
+    begin
+        ioReq^.io_Message.mn_Node.ln_Type := $FF;
+        ioReq^.io_Message.mn_ReplyPort    := pMsgPort(-1);
+        ioReq^.io_Device                  := pDevice(-1);
+        exec_FreeMem(ioReq, ioReq^.io_Message.mn_Length);
+    end
+end;
+
+function Createport(name : PChar; pri : longint): pMsgPort;
+var
+   sigbit : ShortInt;
+   port    : pMsgPort;
+begin
+   sigbit := exec_AllocSignal(-1);
+   if sigbit = -1 then CreatePort := nil;
+   port := exec_Allocmem(sizeof(tMsgPort),MEMF_CLEAR or MEMF_PUBLIC);
+   if port = nil then begin
+      exec_FreeSignal(sigbit);
+      CreatePort := nil;
+   end;
+   with port^ do begin
+       if assigned(name) then
+       mp_Node.ln_Name := name
+       else mp_Node.ln_Name := nil;
+       mp_Node.ln_Pri := pri;
+       mp_Node.ln_Type := 4;
+       mp_Flags := 0;
+       mp_SigBit := sigbit;
+       mp_SigTask := exec_FindTask(nil);
+   end;
+   if assigned(name) then exec_AddPort(port)
+   else NewList(addr(port^.mp_MsgList));
+   CreatePort := port;
+end;
+
+procedure DeletePort (port: pMsgPort);
+begin
+    if port <> NIL then
+    begin
+        if port^.mp_Node.ln_Name <> NIL then
+            exec_RemPort(port);
+
+        port^.mp_Node.ln_Type     := $FF;
+        port^.mp_MsgList.lh_Head  := pNode(-1);
+        exec_FreeSignal(port^.mp_SigBit);
+        exec_FreeMem(port, sizeof(tMsgPort));
+    end;
+end;
+
+
+Function Create_Timer(theUnit : longint) : pTimeRequest;
+var
+    Error : longint;
+    TimerPort : pMsgPort;
+    TimeReq : pTimeRequest;
+begin
+    TimerPort := CreatePort(Nil, 0);
+    if TimerPort = Nil then 
+  Create_Timer := Nil;
+    TimeReq := pTimeRequest(CreateExtIO(TimerPort,sizeof(tTimeRequest)));
+    if TimeReq = Nil then begin
+  DeletePort(TimerPort);
+  Create_Timer := Nil;
+    end; 
+    Error := exec_OpenDevice(TIMERNAME, theUnit, pIORequest(TimeReq), 0);
+    if Error <> 0 then begin
+  DeleteExtIO(pIORequest(TimeReq));
+  DeletePort(TimerPort);
+  Create_Timer := Nil;
+    end;
+    TimerBase := pointer(TimeReq^.tr_Node.io_Device); 
+    Create_Timer := pTimeRequest(TimeReq);
+end;
+
+Procedure Delete_Timer(WhichTimer : pTimeRequest);
+var
+    WhichPort : pMsgPort;
+begin
+    
+    WhichPort := WhichTimer^.tr_Node.io_Message.mn_ReplyPort;
+    if assigned(WhichTimer) then begin
+        exec_CloseDevice(pIORequest(WhichTimer));
+        DeleteExtIO(pIORequest(WhichTimer));
+    end;
+    if assigned(WhichPort) then
+        DeletePort(WhichPort);
+end;
+
+function set_new_time(secs, micro : longint): longint;
+var
+    tr : ptimerequest;
+begin
+    tr := create_timer(UNIT_MICROHZ);
+
+    { non zero return says error }
+    if tr = nil then set_new_time := -1;
+  
+    tr^.tr_time.tv_secs := secs;
+    tr^.tr_time.tv_micro := micro;
+    tr^.tr_node.io_Command := TR_SETSYSTIME;
+    exec_DoIO(pIORequest(tr));
+
+    delete_timer(tr);
+    set_new_time := 0;
+end;
+
+function get_sys_time(tv : ptimeval): longint;
+var
+    tr : ptimerequest;
+begin
+    tr := create_timer( UNIT_MICROHZ );
+
+    { non zero return says error }
+    if tr = nil then get_sys_time := -1;
+
+    tr^.tr_node.io_Command := TR_GETSYSTIME;
+    exec_DoIO(pIORequest(tr));
+
+   { structure assignment }
+   tv^ := tr^.tr_time;
+
+   delete_timer(tr);
+   get_sys_time := 0;
+end;
+
+Procedure GetDate(Var Year, Month, MDay, WDay: Word);
+Var
+  cd    : pClockData;
+  oldtime : ttimeval;
+begin
+  New(cd);
+  get_sys_time(@oldtime);
+  util_Amiga2Date(oldtime.tv_secs,cd);
+  Year  := cd^.year;
+  Month := cd^.month;
+  MDay  := cd^.mday;
+  WDay  := cd^.wday;
+  Dispose(cd);
+end;
+
+Procedure SetDate(Year, Month, Day: Word);
+var
+  cd : pClockData;
+  oldtime : ttimeval;
+Begin
+  new(cd);
+  get_sys_time(@oldtime);
+  util_Amiga2Date(oldtime.tv_secs,cd);
+  cd^.year := Year;
+  cd^.month := Month;
+  cd^.mday := Day;
+  set_new_time(util_Date2Amiga(cd),0);
+  dispose(cd);
+  End;
+
+Procedure GetTime(Var Hour, Minute, Second, Sec100: Word);
+Var
+  cd      : pClockData;
+  oldtime : ttimeval;
+begin
+  New(cd);
+  get_sys_time(@oldtime);
+  util_Amiga2Date(oldtime.tv_secs,cd);
+  Hour   := cd^.hour;
+  Minute := cd^.min;
+  Second := cd^.sec;
+  Sec100 := oldtime.tv_micro div 10000;
+  Dispose(cd);
+END;
+
+
+Procedure SetTime(Hour, Minute, Second, Sec100: Word);
+var
+  cd : pClockData;
+  oldtime : ttimeval;
+Begin
+  new(cd);
+  get_sys_time(@oldtime);
+  util_Amiga2Date(oldtime.tv_secs,cd);
+  cd^.hour := Hour;
+  cd^.min := Minute;
+  cd^.sec := Second;
+  set_new_time(util_Date2Amiga(cd), Sec100 * 10000);
+  dispose(cd);
+  End;
+
+Procedure unpacktime(p : longint;var t : datetime);
+Begin
+  AmigaToDt(p,t);
+End;
+
+
+Procedure packtime(var t : datetime;var p : longint);
+Begin
+  p := DtToAmiga(t);
+end;
+
+
+{******************************************************************************
+                               --- Exec ---
+******************************************************************************}
+
+
+Var
+  LastDosExitCode: word;
+  Ver : Boolean;
+
+
+Procedure Exec (Const Path: PathStr; Const ComLine: ComStr);
+  var
+   p : string;
+   buf: array[0..255] of char;
+   result : longint;
+   MyLock : longint;
+   i : Integer;
+  Begin
+   DosError := 0;
+   LastdosExitCode := 0;
+   p:=Path+' '+ComLine;
+   { allow backslash as slash }
+   for i:=1 to length(p) do
+       if p[i]='\' then p[i]:='/';
+   Move(p[1],buf,length(p));
+   buf[Length(p)]:=#0;
+   { Here we must first check if the command we wish to execute }
+   { actually exists, because this is NOT handled by the        }
+   { _SystemTagList call (program will abort!!)                 }
+
+   { Try to open with shared lock                               }
+   MyLock:=Lock(Path,SHARED_LOCK);
+   if MyLock <> 0 then
+     Begin
+        { File exists - therefore unlock it }
+        dos_Unlock(MyLock);
+        result:=dos_SystemTagList(buf,nil);
+        { on return of -1 the shell could not be executed }
+        { probably because there was not enough memory    }
+        if result = -1 then
+          DosError:=8
+        else
+          LastDosExitCode:=word(result);
+     end
+   else
+    DosError:=3;
+  End;
+
+
+Function DosExitCode: Word;
+  Begin
+    DosExitCode:=LastdosExitCode;
+  End;
+
+
+  Procedure GetCBreak(Var BreakValue: Boolean);
+  Begin
+   breakvalue := system.BreakOn;
+  End;
+
+
+ Procedure SetCBreak(BreakValue: Boolean);
+  Begin
+   system.Breakon := BreakValue;
+  End;
+
+
+  Procedure GetVerify(Var Verify: Boolean);
+   Begin
+     verify:=ver;
+   End;
+
+
+ Procedure SetVerify(Verify: Boolean);
+  Begin
+    ver:=Verify;
+  End;
+
+{******************************************************************************
+                               --- Disk ---
+******************************************************************************}
+
+{ How to solve the problem with this:       }
+{  We could walk through the device list    }
+{  at startup to determine possible devices }
+
+const
+
+  not_to_use_devs : array[0..12] of string =(
+                   'DF0:',
+                   'DF1:',
+                   'DF2:',
+                   'DF3:',
+                   'PED:',
+                   'PRJ:',
+                   'PIPE:',
+                   'RAM:',
+                   'CON:',
+                   'RAW:',
+                   'SER:',
+                   'PAR:',
+                   'PRT:');
+
+var
+   deviceids : array[1..20] of byte;
+   devicenames : array[1..20] of string[20];
+   numberofdevices : Byte;
+
+Function DiskFree(Drive: Byte): Longint;
+Var
+  MyLock      : BPTR;
+  Inf         : pInfoData;
+  Free        : Longint;
+  myproc      : pProcess;
+  OldWinPtr   : Pointer;
+Begin
+  Free := -1;
+  { Here we stop systemrequesters to appear }
+  myproc := pProcess(exec_FindTask(nil));
+  OldWinPtr := myproc^.pr_WindowPtr;
+  myproc^.pr_WindowPtr := Pointer(-1);
+  { End of systemrequesterstop }
+  New(Inf);
+  MyLock := Lock(devicenames[deviceids[Drive]],SHARED_LOCK);
+  If MyLock <> 0 then begin
+     if dos_Info(MyLock,Inf) then begin
+        Free := (Inf^.id_NumBlocks * Inf^.id_BytesPerBlock) -
+                (Inf^.id_NumBlocksUsed * Inf^.id_BytesPerBlock);
+     end;
+     dos_Unlock(MyLock);
+  end;
+  Dispose(Inf);
+  { Restore systemrequesters }
+  myproc^.pr_WindowPtr := OldWinPtr;
+  diskfree := Free;
+end;
+
+
+
+Function DiskSize(Drive: Byte): Longint;
+Var
+  MyLock      : BPTR;
+  Inf         : pInfoData;
+  Size        : Longint;
+  myproc      : pProcess;
+  OldWinPtr   : Pointer;
+Begin
+  Size := -1;
+  { Here we stop systemrequesters to appear }
+  myproc := pProcess(exec_FindTask(nil));
+  OldWinPtr := myproc^.pr_WindowPtr;
+  myproc^.pr_WindowPtr := Pointer(-1);
+  { End of systemrequesterstop }
+  New(Inf);
+  MyLock := Lock(devicenames[deviceids[Drive]],SHARED_LOCK);
+  If MyLock <> 0 then begin
+     if dos_Info(MyLock,Inf) then begin
+        Size := (Inf^.id_NumBlocks * Inf^.id_BytesPerBlock);
+     end;
+     dos_Unlock(MyLock);
+  end;
+  Dispose(Inf);
+  { Restore systemrequesters }
+  myproc^.pr_WindowPtr := OldWinPtr;
+  disksize := Size;
+end;
+
+
+
+
+Procedure FindFirst(Path: PathStr; Attr: Word; Var f: SearchRec);
+var
+ buf: Array[0..255] of char;
+ Anchor : pAnchorPath;
+ Result : Longint;
+ index : Integer;
+ s     : string;
+ j     : integer;
+Begin
+ DosError:=0;
+ New(Anchor);
+ {----- allow backslash as slash         -----}
+ for index:=1 to length(path) do
+   if path[index]='\' then path[index]:='/';
+ { remove any dot characters and replace by their current }
+ { directory equivalent.                                  }
+ if pos('../',path) = 1 then
+   begin
+     getdir(0,s);
+     while pos('../',path) = 1 do
+     { look for parent directory }
+      Begin
+         delete(path,1,3);
+         j:=length(s);
+         while (s[j] <> '/') AND (s[j] <> ':') AND (j > 0 ) do
+           dec(j);
+         if j > 0 then
+           s:=copy(s,1,j-1);
+      end;
+     if (length(s) <> 0) and (s[length(s)] <> ':') then
+       s:=s + '/';
+     path:=s+path;
+  end
+ else
+ if pos('./',path) = 1 then
+ { look for current directory }
+    Begin
+       delete(path,1,2);
+       getdir(0,s);
+       if (s[length(s)] <> '/') and (s[length(s)] <> ':') then
+          s:=s+'/';
+       path:=s+path;
+    end;
+ {----- replace * by #? AmigaOs strings  -----}
+ repeat
+  index:= pos('*',Path);
+  if index <> 0 then
+   Begin
+     delete(Path,index,1);
+     insert('#?',Path,index);
+   end;
+ until index =0;
+ {--------------------------------------------}
+ FillChar(Anchor^,sizeof(TAnchorPath),#0);
+ move(path[1],buf,length(path));
+ buf[length(path)]:=#0;
+
+ Result:=dos_MatchFirst(@buf,Anchor);
+ f.AnchorPtr:=Anchor;
+ if Result = ERROR_NO_MORE_ENTRIES then
+   DosError:=18
+ else
+ if Result <> 0 then
+   DosError:=3;
+ { If there is an error, deallocate }
+ { the anchorpath structure         }
+ if DosError <> 0 then
+   Begin
+     dos_MatchEnd(Anchor);
+     if assigned(Anchor) then
+       Dispose(Anchor);
+   end
+ else
+ {-------------------------------------------------------------------}
+ { Here we fill up the SearchRec attribute, but we also do check     }
+ { something else, if the it does not match the mask we are looking  }
+ { for we should go to the next file or directory.                   }
+ {-------------------------------------------------------------------}
+   Begin
+         with Anchor^.ap_Info do
+          Begin
+             f.Time := fib_Date.ds_Days * (24 * 60 * 60) +
+             fib_Date.ds_Minute * 60 +
+             fib_Date.ds_Tick div 50;
+           {*------------------------------------*}
+           {* Determine if is a file or a folder *}
+           {*------------------------------------*}
+           if fib_DirEntryType > 0 then
+               f.attr:=f.attr OR DIRECTORY;
+
+           {*------------------------------------*}
+           {* Determine if Read only             *}
+           {*  Readonly if R flag on and W flag  *}
+           {*   off.                             *}
+           {* Should we check also that EXEC     *}
+           {* is zero? for read only?            *}
+           {*------------------------------------*}
+           if   ((fib_Protection and FIBF_READ) <> 0)
+            AND ((fib_Protection and FIBF_WRITE) = 0)
+           then
+              f.attr:=f.attr or READONLY;
+           f.Name := strpas(fib_FileName);
+           f.Size := fib_Size;
+         end; { end with }
+   end;
+End;
+
+
+Procedure FindNext(Var f: SearchRec);
+var
+ Result: longint;
+ Anchor : pAnchorPath;
+Begin
+ DosError:=0;
+ Result:=dos_MatchNext(f.AnchorPtr);
+ if Result = ERROR_NO_MORE_ENTRIES then
+   DosError:=18
+ else
+ if Result <> 0 then
+   DosError:=3;
+ { If there is an error, deallocate }
+ { the anchorpath structure         }
+ if DosError <> 0 then
+   Begin
+     dos_MatchEnd(f.AnchorPtr);
+     if assigned(f.AnchorPtr) then
+       {Dispose}FreeMem(f.AnchorPtr);
+   end
+ else
+ { Fill up the Searchrec information     }
+ { and also check if the files are with  }
+ { the correct attributes                }
+   Begin
+         Anchor:=pAnchorPath(f.AnchorPtr);
+         with Anchor^.ap_Info do
+          Begin
+             f.Time := fib_Date.ds_Days * (24 * 60 * 60) +
+             fib_Date.ds_Minute * 60 +
+             fib_Date.ds_Tick div 50;
+           {*------------------------------------*}
+           {* Determine if is a file or a folder *}
+           {*------------------------------------*}
+           if fib_DirEntryType > 0 then
+               f.attr:=f.attr OR DIRECTORY;
+
+           {*------------------------------------*}
+           {* Determine if Read only             *}
+           {*  Readonly if R flag on and W flag  *}
+           {*   off.                             *}
+           {* Should we check also that EXEC     *}
+           {* is zero? for read only?            *}
+           {*------------------------------------*}
+           if   ((fib_Protection and FIBF_READ) <> 0)
+            AND ((fib_Protection and FIBF_WRITE) = 0)
+           then
+              f.attr:=f.attr or READONLY;
+           f.Name := strpas(fib_FileName);
+           f.Size := fib_Size;
+         end; { end with }
+   end;
+End;
+
+    Procedure FindClose(Var f: SearchRec);
+      begin
+      end;
+
+{******************************************************************************
+                               --- File ---
+******************************************************************************}
+Procedure FSplit(path: pathstr; var dir: dirstr; var name: namestr; var ext: extstr);
+var
+  I: Word;
+begin
+  { allow backslash as slash }
+  for i:=1 to length(path) do
+    if path[i]='\' then path[i]:='/';
+
+  I := Length(Path);
+  while (I > 0) and not ((Path[I] = '/') or (Path[I] = ':'))
+     do Dec(I);
+  if Path[I] = '/' then
+     dir := Copy(Path, 0, I)
+  else dir := Copy(Path,0,I);
+
+  if Length(Path) > Length(dir) then
+      name := Copy(Path, I + 1, Length(Path)-I)
+  else
+      name := '';
+  { Remove extension }
+  if pos('.',name) <> 0 then
+   begin
+     ext:=copy(name,pos('.',name),length(name));
+     delete(name,pos('.',name),length(name));
+   end
+ else
+   ext := '';
+end;
+
+{$DEFINE FPC_FEXPAND_VOLUMES} (* Full paths begin with drive specification *)
+{$DEFINE FPC_FEXPAND_DRIVESEP_IS_ROOT}
+{$DEFINE FPC_FEXPAND_NO_DEFAULT_PATHS}
+{$I fexpand.inc}
+
+{$UNDEF FPC_FEXPAND_VOLUMES} (* Full paths begin with drive specification *)
+{$UNDEF FPC_FEXPAND_DRIVESEP_IS_ROOT}
+{$UNDEF FPC_FEXPAND_NO_DEFAULT_PATHS}
+
+
+
+   Function  fsearch(path : pathstr;dirlist : string) : pathstr;
+      var
+         i,p1   : longint;
+         s      : searchrec;
+         newdir : pathstr;
+      begin
+      { No wildcards allowed in these things }
+         if (pos('?',path)<>0) or (pos('*',path)<>0) then
+           fsearch:=''
+         else
+           begin
+              { allow slash as backslash }
+              for i:=1 to length(dirlist) do
+                if dirlist[i]='\' then dirlist[i]:='/';
+              repeat
+                p1:=pos(';',dirlist);
+                if p1<>0 then
+                 begin
+                   newdir:=copy(dirlist,1,p1-1);
+                   delete(dirlist,1,p1);
+                 end
+                else
+                 begin
+                   newdir:=dirlist;
+                   dirlist:='';
+                 end;
+                if (newdir<>'') and (not (newdir[length(newdir)] in ['/',':'])) then
+                 newdir:=newdir+'/';
+                findfirst(newdir+path,anyfile,s);
+                if doserror=0 then
+                 newdir:=newdir+path
+                else
+                 newdir:='';
+              until (dirlist='') or (newdir<>'');
+              fsearch:=newdir;
+           end;
+      end;
+
+
+Procedure getftime (var f; var time : longint);
+{
+    This function returns a file's date and time as the number of
+    seconds after January 1, 1978 that the file was created.
+}
+var
+    FInfo : pFileInfoBlock;
+    FTime : Longint;
+    FLock : Longint;
+    Str   : String;
+    i     : integer;
+begin
+    DosError:=0;
+    FTime := 0;
+    Str := StrPas(filerec(f).name);
+    for i:=1 to length(Str) do
+     if str[i]='\' then str[i]:='/';
+    FLock := Lock(Str, SHARED_LOCK);
+    IF FLock <> 0 then begin
+        New(FInfo);
+        if dos_Examine(FLock, FInfo) then begin
+             with FInfo^.fib_Date do
+             FTime := ds_Days * (24 * 60 * 60) +
+             ds_Minute * 60 +
+             ds_Tick div 50;
+        end else begin
+             FTime := 0;
+        end;
+        dos_Unlock(FLock);
+        Dispose(FInfo);
+    end
+    else
+     DosError:=6;
+    time := FTime;
+end;
+
+
+  Procedure setftime(var f; time : longint);
+   var
+    DateStamp: pDateStamp;
+    Str: String;
+    i: Integer;
+    Days, Minutes,Ticks: longint;
+    FLock: longint;
+  Begin
+    new(DateStamp);
+    Str := StrPas(filerec(f).name);
+    for i:=1 to length(Str) do
+     if str[i]='\' then str[i]:='/';
+    { Check first of all, if file exists }
+    FLock := Lock(Str, SHARED_LOCK);
+    IF FLock <> 0 then
+      begin
+        dos_Unlock(FLock);
+        Amiga2DateStamp(time,Days,Minutes,ticks);
+        DateStamp^.ds_Days:=Days;
+        DateStamp^.ds_Minute:=Minutes;
+        DateStamp^.ds_Tick:=Ticks;
+        if SetFileDate(Str,DateStamp) then
+            DosError:=0
+        else
+            DosError:=6;
+      end
+    else
+      DosError:=2;
+    if assigned(DateStamp) then Dispose(DateStamp);
+  End;
+
+  Procedure getfattr(var f; var attr : word);
+  var
+    info : pFileInfoBlock;
+    MyLock : Longint;
+    flags: word;
+    Str: String;
+    i: integer;
+  Begin
+    DosError:=0;
+    flags:=0;
+    New(info);
+    Str := StrPas(filerec(f).name);
+    for i:=1 to length(Str) do
+     if str[i]='\' then str[i]:='/';
+    { open with shared lock to check if file exists }
+    MyLock:=Lock(Str,SHARED_LOCK);
+    if MyLock <> 0 then
+      Begin
+        dos_Examine(MyLock,info);
+        {*------------------------------------*}
+        {* Determine if is a file or a folder *}
+        {*------------------------------------*}
+        if info^.fib_DirEntryType > 0 then
+             flags:=flags OR DIRECTORY;
+
+        {*------------------------------------*}
+        {* Determine if Read only             *}
+        {*  Readonly if R flag on and W flag  *}
+        {*   off.                             *}
+        {* Should we check also that EXEC     *}
+        {* is zero? for read only?            *}
+        {*------------------------------------*}
+        if   ((info^.fib_Protection and FIBF_READ) <> 0)
+         AND ((info^.fib_Protection and FIBF_WRITE) = 0)
+         then
+          flags:=flags OR ReadOnly;
+        dos_Unlock(mylock);
+      end
+    else
+      DosError:=3;
+    attr:=flags;
+    Dispose(info);
+  End;
+
+
+Procedure setfattr (var f;attr : word);
+  var
+   flags: longint;
+   MyLock : longint;
+   str: string;
+   i: integer;
+  Begin
+    DosError:=0;
+    flags:=FIBF_WRITE;
+    { open with shared lock }
+    Str := StrPas(filerec(f).name);
+    for i:=1 to length(Str) do
+     if str[i]='\' then str[i]:='/';
+
+    MyLock:=Lock(Str,SHARED_LOCK);
+
+    { By default files are read-write }
+    if attr AND ReadOnly <> 0 then
+      { Clear the Fibf_write flags }
+      flags:=FIBF_READ;
+
+
+    if MyLock <> 0 then
+     Begin
+       dos_Unlock(MyLock);
+       if Not SetProtection(Str,flags) then
+         DosError:=5;
+     end
+    else
+      DosError:=3;
+  End;
+
+
+
+{******************************************************************************
+                             --- Environment ---
+******************************************************************************}
+
+var
+StrofPaths : string[255];
+
+function getpathstring: string;
+var
+   f : text;
+   s : string;
+   found : boolean;
+   temp : string[255];
+begin
+   found := true;
+   temp := '';
+   assign(f,'ram:makepathstr');
+   rewrite(f);
+   writeln(f,'path >ram:temp.lst');
+   close(f);
+   exec('c:protect','ram:makepathstr sarwed');
+   exec('C:execute','ram:makepathstr');
+   exec('c:delete','ram:makepathstr quiet');
+   assign(f,'ram:temp.lst');
+   reset(f);
+   { skip the first line, garbage }
+   if not eof(f) then readln(f,s);
+   while not eof(f) do begin
+      readln(f,s);
+      if found then begin
+         temp := s;
+         found := false;
+      end else begin;
+         if (length(s) + length(temp)) < 255 then
+            temp := temp + ';' + s;
+      end;
+   end;
+   close(f);
+   exec('C:delete','ram:temp.lst quiet');
+   getpathstring := temp;
+end;
+
+
+ Function EnvCount: Longint;
+ { HOW TO GET THIS VALUE:                                }
+ {   Each time this function is called, we look at the   }
+ {   local variables in the Process structure (2.0+)     }
+ {   And we also read all files in the ENV: directory    }
+  Begin
+   EnvCount := 0;
+  End;
+
+
+ Function EnvStr(Index: Integer): String;
+  Begin
+    EnvStr:='';
+  End;
+
+
+
+function GetEnv(envvar : String): String;
+var
+   bufarr : array[0..255] of char;
+   strbuffer : array[0..255] of char;
+   temp : Longint;
+begin
+   if UpCase(envvar) = 'PATH' then begin
+       if StrOfpaths = '' then StrOfPaths := GetPathString;
+       GetEnv := StrofPaths;
+   end else begin
+      move(envvar,strbuffer,length(envvar));
+      strbuffer[length(envvar)] := #0;
+      temp := dos_GetVar(strbuffer,bufarr,255,$100);
+      if temp = -1 then
+        GetEnv := ''
+      else GetEnv := StrPas(bufarr);
+   end;
+end;
+
+
+{******************************************************************************
+                             --- Not Supported ---
+******************************************************************************}
+
+Procedure keep(exitcode : word);
+  Begin
+  { ! Not implemented in Linux ! }
+  End;
+
+procedure AddDevice(str : String);
+begin
+    inc(numberofdevices);
+    deviceids[numberofdevices] := numberofdevices;
+    devicenames[numberofdevices] := str;
+end;
+
+function MakeDeviceName(str : pchar): string;
+var
+   temp : string[20];
+begin
+   temp := strpas(str);
+   temp := temp + ':';
+   MakeDeviceName := temp;
+end;
+
+function IsInDeviceList(str : string): boolean;
+var
+   i : byte;
+   theresult : boolean;
+begin
+   theresult := false;
+   for i := low(not_to_use_devs) to high(not_to_use_devs) do
+   begin
+       if str = not_to_use_devs[i] then begin
+          theresult := true;
+          break;
+       end;
+   end;
+   IsInDeviceList := theresult;
+end;
+
+
+function BSTR2STRING(s : BSTR): pchar;
+begin
+    BSTR2STRING := Pointer(Longint(BADDR(s))+1);
+end;
+
+procedure ReadInDevices;
+var
+   dl : pDosList;
+   temp : pchar;
+   str  : string[20];
+begin
+   dl := dos_LockDosList(LDF_DEVICES or LDF_READ );
+   repeat
+      dl := dos_NextDosEntry(dl,LDF_DEVICES );
+      if dl <> nil then begin
+         temp := BSTR2STRING(dl^.dol_Name);
+         str := MakeDeviceName(temp);
+         if not IsInDeviceList(str) then
+              AddDevice(str);
+      end;
+   until dl = nil;
+   dos_UnLockDosList(LDF_DEVICES or LDF_READ );
+end;
+
+Begin
+ DosError:=0;
+ ver := TRUE;
+ numberofdevices := 0;
+ StrOfPaths := '';
+ AddDevice('DF0:');
+ AddDevice('DF1:');
+ AddDevice('DF2:');
+ AddDevice('DF3:');
+ ReadInDevices;
+End.
+
+{
+  $Log$
+  Revision 1.1  2004-05-12 20:27:29  karoly
+    * first implementation of MorphOS DOS unit, based on Amiga version
+
+}