{ This file is part of the Free Pascal run time library. Copyright (c) 2016 by Free Pascal development team Sysutils unit for Atari 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. **********************************************************************} {$IFNDEF FPC_DOTTEDUNITS} unit sysutils; {$ENDIF FPC_DOTTEDUNITS} interface {$MODE objfpc} {$MODESWITCH OUT} {$IFDEF UNICODERTL} {$MODESWITCH UNICODESTRINGS} {$ELSE} {$H+} {$ENDIF} {$modeswitch typehelpers} {$modeswitch advancedrecords} {$DEFINE OS_FILESETDATEBYNAME} {$DEFINE HAS_SLEEP} {$DEFINE HAS_OSERROR} {OS has only 1 byte version for ExecuteProcess} {$define executeprocuni} { used OS file system APIs use ansistring } {$define SYSUTILS_HAS_ANSISTR_FILEUTIL_IMPL} { OS has an ansistring/single byte environment variable API } {$define SYSUTILS_HAS_ANSISTR_ENVVAR_IMPL} { Include platform independent interface part } {$i sysutilh.inc} { Platform dependent calls } implementation {$IFDEF FPC_DOTTEDUNITS} uses { TP.DOS,} System.SysConst; {$ELSE FPC_DOTTEDUNITS} uses { dos,} sysconst; {$ENDIF FPC_DOTTEDUNITS} {$DEFINE FPC_FEXPAND_UNC} (* UNC paths are supported *) {$DEFINE FPC_FEXPAND_DRIVES} (* Full paths begin with drive specification *) { Include platform independent implementation part } {$i sysutils.inc} {$i gemdos.inc} var basepage: PPD; external name '__base'; {**************************************************************************** File Functions ****************************************************************************} {$I-}{ Required for correct usage of these routines } (****** non portable routines ******) function FileOpen(const FileName: rawbytestring; Mode: Integer): THandle; begin { Mode has some Share modes. Maybe something for MiNT? } { Lower three bits of Mode are actually TOS compatible } FileOpen:=gemdos_fopen(PAnsiChar(FileName), Mode and 3); if FileOpen < -1 then FileOpen:=-1; end; function FileGetDate(Handle: THandle) : Int64; var td: TDOSTIME; begin { Fdatime doesn't report errors... } gemdos_fdatime(@td,handle,0); result:=(td.date shl 16) or td.time; end; function FileSetDate(Handle: THandle; Age: Int64) : LongInt; var td: TDOSTIME; begin td.date:=(Age shr 16) and $ffff; td.time:=Age and $ffff; gemdos_fdatime(@td,handle,1); { Fdatime doesn't report errors... } result:=0; end; function FileSetDate(const FileName: RawByteString; Age: Int64) : LongInt; var f: THandle; begin FileSetDate:=-1; f:=FileOpen(FileName,fmOpenReadWrite); if f < 0 then exit; FileSetDate(f,Age); FileClose(f); end; function FileCreate(const FileName: RawByteString) : THandle; begin FileCreate:=gemdos_fcreate(PAnsiChar(FileName),0); if FileCreate < -1 then FileCreate:=-1; end; function FileCreate(const FileName: RawByteString; Rights: integer): THandle; begin { Rights are Un*x extension. Maybe something for MiNT? } FileCreate:=FileCreate(FileName); end; function FileCreate(const FileName: RawByteString; ShareMode: integer; Rights : integer): THandle; begin { Rights and ShareMode are Un*x extension. Maybe something for MiNT? } FileCreate:=FileCreate(FileName); end; function FileRead(Handle: THandle; out Buffer; Count: LongInt): LongInt; begin FileRead:=-1; if (Count<=0) then exit; FileRead:=gemdos_fread(handle, count, @buffer); if FileRead < -1 then FileRead:=-1; end; function FileWrite(Handle: THandle; const Buffer; Count: LongInt): LongInt; begin FileWrite:=-1; if (Count<=0) then exit; FileWrite:=gemdos_fwrite(handle, count, @buffer); if FileWrite < -1 then FileWrite:=-1; end; function FileSeek(Handle: THandle; FOffset, Origin: LongInt) : LongInt; var dosResult: longint; begin FileSeek:=-1; { TOS seek mode flags are actually compatible to DOS/TP } dosResult:=gemdos_fseek(FOffset, Handle, Origin); if dosResult < 0 then exit; FileSeek:=dosResult; end; function FileSeek(Handle: THandle; FOffset: Int64; Origin: Longint): Int64; begin FileSeek:=FileSeek(Handle,LongInt(FOffset),Origin); end; procedure FileClose(Handle: THandle); begin gemdos_fclose(handle); end; function FileTruncate(Handle: THandle; Size: Int64): Boolean; begin FileTruncate:=False; end; function DeleteFile(const FileName: RawByteString) : Boolean; begin DeleteFile:=gemdos_fdelete(PAnsiChar(FileName)) >= 0; end; function RenameFile(const OldName, NewName: RawByteString): Boolean; begin RenameFile:=gemdos_frename(0,PAnsiChar(oldname),PAnsiChar(newname)) >= 0; end; (****** end of non portable routines ******) function FileAge (const FileName : RawByteString): Int64; var f: THandle; begin FileAge:=-1; f:=FileOpen(FileName,fmOpenRead); if f < 0 then exit; FileAge:=FileGetDate(f); FileClose(f); end; function FileGetSymLinkTarget(const FileName: RawByteString; out SymLinkRec: TRawbyteSymLinkRec): Boolean; begin Result := False; end; function FileExists (const FileName : RawByteString; FollowLink : Boolean) : Boolean; var Attr: longint; begin FileExists:=false; Attr:=FileGetAttr(FileName); if Attr < 0 then exit; result:=(Attr and (faVolumeID or faDirectory)) = 0; end; type PInternalFindData = ^TInternalFindData; TInternalFindData = record dta_original: pointer; dta_search: TDTA; end; Function InternalFindFirst (Const Path : RawByteString; Attr : Longint; out Rslt : TAbstractSearchRec; var Name: RawByteString) : Longint; var dosResult: longint; IFD: PInternalFindData; begin result:=-1; { We emulate Linux/Unix behaviour, and return -1 on errors. } new(IFD); IFD^.dta_original:=gemdos_getdta; gemdos_setdta(@IFD^.dta_search); Rslt.FindHandle:=nil; dosResult:=gemdos_fsfirst(PAnsiChar(path), Attr and faAnyFile); if dosResult < 0 then begin InternalFindClose(IFD); exit; end; Rslt.FindHandle:=IFD; with IFD^.dta_search do begin Name:=d_fname; SetCodePage(Name,DefaultFileSystemCodePage,false); Rslt.Time:=(d_date shl 16) or d_time; Rslt.Size:=d_length; { "128" is Windows "NORMALFILE" attribute. Some buggy code depend on this... :( (KB) } Rslt.Attr := 128 or d_attrib; end; result:=0; end; Function InternalFindNext (var Rslt : TAbstractSearchRec; var Name : RawByteString) : Longint; var dosResult: longint; IFD: PInternalFindData; begin result:=-1; IFD:=PInternalFindData(Rslt.FindHandle); if not assigned(IFD) then exit; dosResult:=gemdos_fsnext; if dosResult < 0 then exit; with IFD^.dta_search do begin Name:=d_fname; SetCodePage(Name,DefaultFileSystemCodePage,false); Rslt.Time:=(d_date shl 16) or d_time; Rslt.Size:=d_length; { "128" is Windows "NORMALFILE" attribute. Some buggy code depend on this... :( (KB) } Rslt.Attr := 128 or d_attrib; end; result:=0; end; Procedure InternalFindClose(var Handle: Pointer); var IFD: PInternalFindData; begin IFD:=PInternalFindData(Handle); if not assigned(IFD) then exit; gemdos_setdta(IFD^.dta_original); dispose(IFD); IFD:=nil; end; (****** end of non portable routines ******) Function FileGetAttr (Const FileName : RawByteString) : Longint; begin FileGetAttr:=gemdos_fattrib(PAnsiChar(FileName),0,0); end; Function FileSetAttr (Const Filename : RawByteString; Attr: longint) : Longint; begin FileSetAttr:=gemdos_fattrib(PAnsiChar(FileName),1,Attr and faAnyFile); if FileSetAttr < -1 then FileSetAttr:=-1 else FileSetAttr:=0; end; {**************************************************************************** Disk Functions ****************************************************************************} function DiskSize(Drive: Byte): Int64; var dosResult: longint; di: TDISKINFO; begin DiskSize := -1; dosResult:=gemdos_dfree(@di,drive); if dosResult < 0 then exit; DiskSize:=di.b_total * di.b_secsiz * di.b_clsiz; end; function DiskFree(Drive: Byte): Int64; var dosResult: longint; di: TDISKINFO; begin DiskFree := -1; dosResult:=gemdos_dfree(@di,drive); if dosResult < 0 then exit; DiskFree:=di.b_free * di.b_secsiz * di.b_clsiz; end; function DirectoryExists(const Directory: RawByteString; FollowLink : Boolean): Boolean; var Attr: longint; begin DirectoryExists:=false; Attr:=FileGetAttr(Directory); if Attr < 0 then exit; result:=(Attr and faDirectory) <> 0; end; {**************************************************************************** Locale Functions ****************************************************************************} Procedure GetLocalTime(var SystemTime: TSystemTime); var TOSTime: Longint; begin LongRec(TOSTime).hi:=gemdos_tgetdate; LongRec(TOSTime).lo:=gemdos_tgettime; DateTimeToSystemTime(FileDateToDateTime(TOSTime),SystemTime); end; Procedure InitAnsi; Var i : longint; begin { Fill table entries 0 to 127 } for i := 0 to 96 do UpperCaseTable[i] := chr(i); for i := 97 to 122 do UpperCaseTable[i] := chr(i - 32); for i := 123 to 191 do UpperCaseTable[i] := chr(i); Move (CPISO88591UCT,UpperCaseTable[192],SizeOf(CPISO88591UCT)); for i := 0 to 64 do LowerCaseTable[i] := chr(i); for i := 65 to 90 do LowerCaseTable[i] := chr(i + 32); for i := 91 to 191 do LowerCaseTable[i] := chr(i); Move (CPISO88591LCT,UpperCaseTable[192],SizeOf(CPISO88591UCT)); end; Procedure InitInternational; begin InitInternationalGeneric; InitAnsi; end; function SysErrorMessage(ErrorCode: Integer): String; begin Result:=Format(SUnknownErrorCode,[ErrorCode]); end; function GetLastOSError: Integer; begin result:=-1; end; {**************************************************************************** OS utility functions ****************************************************************************} function fpGetEnv(const envvar : ShortString): RawByteString; external name '_fpc_atari_getenv'; function GetPathString: String; begin {writeln('Unimplemented GetPathString');} result := ''; end; Function GetEnvironmentVariable(Const EnvVar : String) : String; begin GetEnvironmentVariable := fpgetenv(envvar); end; Function GetEnvironmentVariableCount : Integer; var hp : PAnsiChar; begin result:=0; hp:=basepage^.p_env; If (Hp<>Nil) then while hp^<>#0 do begin Inc(Result); hp:=hp+strlen(hp)+1; end; end; Function GetEnvironmentString(Index : Integer) : {$ifdef FPC_RTL_UNICODE}UnicodeString{$else}AnsiString{$endif}; var hp : PAnsiChar; begin result:=''; hp:=basepage^.p_env; If (Hp<>Nil) then begin while (hp^<>#0) and (Index>1) do begin Dec(Index); hp:=hp+strlen(hp)+1; end; If (hp^<>#0) then begin Result:=hp; end; end; end; function ExecuteProcess (const Path: RawByteString; const ComLine: RawByteString;Flags:TExecuteFlags=[]): integer; var tmpPath: RawByteString; pcmdline: ShortString; CommandLine: RawByteString; E: EOSError; env, s: PAnsiChar; buf, start: PAnsiChar; enlen, len: SizeInt; hp : PAnsiChar; begin tmpPath:=ToSingleByteFileSystemEncodedFileName(Path); pcmdline:=ToSingleByteFileSystemEncodedFileName(ComLine); { count up space needed for environment } enlen := 0; hp:=basepage^.p_env; If (Hp<>Nil) then while hp^<>#0 do begin len := strlen(hp) + 1; inc(enlen, len); inc(hp, len); end; { count up space needed for arguments } len := strlen(PAnsiChar(tmpPath)) + 1; inc(enlen, len); buf := PAnsiChar(ComLine); while (buf^<>#0) do // count nr of args begin while (buf^ in [' ',#9,#10]) do // Kill separators. inc(buf); if buf^=#0 Then break; if buf^='"' Then // quotes argument? begin inc(buf); start := buf; while not (buf^ in [#0,'"']) do // then end of argument is end of string or next quote inc(buf); len := buf - start; if len=0 then len := 1; (* TODO: needs to set NULL environment variable *) inc(len); inc(enlen, len); if buf^='"' then // skip closing quote. inc(buf); end else begin // else std start := buf; while not (buf^ in [' ',#0,#9,#10]) do inc(buf); len := buf - start + 1; inc(enlen, len); end; end; inc(enlen, 64); { filler for stuff like ARGV= and zeros } env := gemdos_malloc(enlen); if env = nil then result := ENSMEM else begin s := env; { copy the environment } hp:=basepage^.p_env; If (Hp<>Nil) then while hp^<>#0 do begin len := strlen(hp) + 1; strcopy(s, hp); inc(hp, len); inc(s, len); end; { start of arguments } strcopy(s, 'ARGV='); inc(s, 6); { s+=sizeof("ARGV=") } { copy argv[0] } buf := PAnsiChar(tmpPath); len := strlen(buf) + 1; strcopy(s, buf); inc(s, len); { copy the parameters } buf:=PAnsiChar(ComLine); while (buf^<>#0) do begin while (buf^ in [' ',#9,#10]) do // Kill separators. inc(buf); if buf^=#0 Then break; if buf^='"' Then // quotes argument? begin inc(buf); start := buf; while not (buf^ in [#0,'"']) do // then end of argument is end of string or next quote begin s^ := buf^; inc(s); inc(buf); end; if buf = start then begin s^ := ' '; inc(s); end; if buf^='"' then // skip closing quote. inc(buf); s^ := #0; inc(s); end else begin start := buf; while not (buf^ in [' ',#0,#9,#10]) do begin s^ := buf^; inc(s); inc(buf); end; s^ := #0; inc(s); end; end; { tie off environment } s^ := #0; inc(s); s^ := #0; { signal Extended Argument Passing } pcmdline[0] := #127; { the zero offset for cmdline is actually correct here. pexec() expects pascal formatted string for cmdline, so length in first byte } result:=gemdos_pexec(0,PAnsiChar(tmpPath),@pcmdline[0],env); gemdos_mfree(env); end; if result < 0 then begin if ComLine = '' then CommandLine := Path else CommandLine := Path + ' ' + ComLine; E := EOSError.CreateFmt (SExecuteProcessFailed, [CommandLine, result]); E.ErrorCode := result; raise E; end; end; function ExecuteProcess (const Path: RawByteString; const ComLine: array of RawByteString;Flags:TExecuteFlags=[]): integer; var CommandLine: RawByteString; I: integer; begin Commandline := ''; for I := 0 to High (ComLine) do if Pos (' ', ComLine [I]) <> 0 then CommandLine := CommandLine + ' ' + '"' + ToSingleByteFileSystemEncodedFileName(ComLine [I]) + '"' else CommandLine := CommandLine + ' ' + ToSingleByteFileSystemEncodedFileName(Comline [I]); ExecuteProcess := ExecuteProcess (Path, CommandLine); end; procedure Sleep(Milliseconds: cardinal); begin {writeln('Unimplemented Sleep');} end; {**************************************************************************** Initialization code ****************************************************************************} Initialization InitExceptions; InitInternational; { Initialize internationalization settings } OnBeep:=Nil; { No SysBeep() on Atari for now. } Finalization FreeTerminateProcs; DoneExceptions; end.