2
0

sysutils.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2016 by Free Pascal development team
  4. Sysutils unit for Atari
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFNDEF FPC_DOTTEDUNITS}
  12. unit sysutils;
  13. {$ENDIF FPC_DOTTEDUNITS}
  14. interface
  15. {$MODE objfpc}
  16. {$MODESWITCH OUT}
  17. {$IFDEF UNICODERTL}
  18. {$MODESWITCH UNICODESTRINGS}
  19. {$ELSE}
  20. {$H+}
  21. {$ENDIF}
  22. {$modeswitch typehelpers}
  23. {$modeswitch advancedrecords}
  24. {$DEFINE OS_FILESETDATEBYNAME}
  25. {$DEFINE HAS_SLEEP}
  26. {$DEFINE HAS_OSERROR}
  27. {OS has only 1 byte version for ExecuteProcess}
  28. {$define executeprocuni}
  29. { used OS file system APIs use ansistring }
  30. {$define SYSUTILS_HAS_ANSISTR_FILEUTIL_IMPL}
  31. { OS has an ansistring/single byte environment variable API }
  32. {$define SYSUTILS_HAS_ANSISTR_ENVVAR_IMPL}
  33. { Include platform independent interface part }
  34. {$i sysutilh.inc}
  35. { Platform dependent calls }
  36. implementation
  37. {$IFDEF FPC_DOTTEDUNITS}
  38. uses
  39. { TP.DOS,} System.SysConst;
  40. {$ELSE FPC_DOTTEDUNITS}
  41. uses
  42. { dos,} sysconst;
  43. {$ENDIF FPC_DOTTEDUNITS}
  44. {$DEFINE FPC_FEXPAND_UNC} (* UNC paths are supported *)
  45. {$DEFINE FPC_FEXPAND_DRIVES} (* Full paths begin with drive specification *)
  46. { Include platform independent implementation part }
  47. {$i sysutils.inc}
  48. {$i gemdos.inc}
  49. var
  50. basepage: PPD; external name '__base';
  51. {****************************************************************************
  52. File Functions
  53. ****************************************************************************}
  54. {$I-}{ Required for correct usage of these routines }
  55. (****** non portable routines ******)
  56. function FileOpen(const FileName: rawbytestring; Mode: Integer): THandle;
  57. begin
  58. { Mode has some Share modes. Maybe something for MiNT? }
  59. { Lower three bits of Mode are actually TOS compatible }
  60. FileOpen:=gemdos_fopen(PAnsiChar(FileName), Mode and 3);
  61. if FileOpen < -1 then
  62. FileOpen:=-1;
  63. end;
  64. function FileGetDate(Handle: THandle) : Int64;
  65. var
  66. td: TDOSTIME;
  67. begin
  68. { Fdatime doesn't report errors... }
  69. gemdos_fdatime(@td,handle,0);
  70. result:=(td.date shl 16) or td.time;
  71. end;
  72. function FileSetDate(Handle: THandle; Age: Int64) : LongInt;
  73. var
  74. td: TDOSTIME;
  75. begin
  76. td.date:=(Age shr 16) and $ffff;
  77. td.time:=Age and $ffff;
  78. gemdos_fdatime(@td,handle,1);
  79. { Fdatime doesn't report errors... }
  80. result:=0;
  81. end;
  82. function FileSetDate(const FileName: RawByteString; Age: Int64) : LongInt;
  83. var
  84. f: THandle;
  85. begin
  86. FileSetDate:=-1;
  87. f:=FileOpen(FileName,fmOpenReadWrite);
  88. if f < 0 then
  89. exit;
  90. FileSetDate(f,Age);
  91. FileClose(f);
  92. end;
  93. function FileCreate(const FileName: RawByteString) : THandle;
  94. begin
  95. FileCreate:=gemdos_fcreate(PAnsiChar(FileName),0);
  96. if FileCreate < -1 then
  97. FileCreate:=-1;
  98. end;
  99. function FileCreate(const FileName: RawByteString; Rights: integer): THandle;
  100. begin
  101. { Rights are Un*x extension. Maybe something for MiNT? }
  102. FileCreate:=FileCreate(FileName);
  103. end;
  104. function FileCreate(const FileName: RawByteString; ShareMode: integer; Rights : integer): THandle;
  105. begin
  106. { Rights and ShareMode are Un*x extension. Maybe something for MiNT? }
  107. FileCreate:=FileCreate(FileName);
  108. end;
  109. function FileRead(Handle: THandle; out Buffer; Count: LongInt): LongInt;
  110. begin
  111. FileRead:=-1;
  112. if (Count<=0) then
  113. exit;
  114. FileRead:=gemdos_fread(handle, count, @buffer);
  115. if FileRead < -1 then
  116. FileRead:=-1;
  117. end;
  118. function FileWrite(Handle: THandle; const Buffer; Count: LongInt): LongInt;
  119. begin
  120. FileWrite:=-1;
  121. if (Count<=0) then
  122. exit;
  123. FileWrite:=gemdos_fwrite(handle, count, @buffer);
  124. if FileWrite < -1 then
  125. FileWrite:=-1;
  126. end;
  127. function FileSeek(Handle: THandle; FOffset, Origin: LongInt) : LongInt;
  128. var
  129. dosResult: longint;
  130. begin
  131. FileSeek:=-1;
  132. { TOS seek mode flags are actually compatible to DOS/TP }
  133. dosResult:=gemdos_fseek(FOffset, Handle, Origin);
  134. if dosResult < 0 then
  135. exit;
  136. FileSeek:=dosResult;
  137. end;
  138. function FileSeek(Handle: THandle; FOffset: Int64; Origin: Longint): Int64;
  139. begin
  140. FileSeek:=FileSeek(Handle,LongInt(FOffset),Origin);
  141. end;
  142. procedure FileClose(Handle: THandle);
  143. begin
  144. gemdos_fclose(handle);
  145. end;
  146. function FileTruncate(Handle: THandle; Size: Int64): Boolean;
  147. begin
  148. FileTruncate:=False;
  149. end;
  150. function DeleteFile(const FileName: RawByteString) : Boolean;
  151. begin
  152. DeleteFile:=gemdos_fdelete(PAnsiChar(FileName)) >= 0;
  153. end;
  154. function RenameFile(const OldName, NewName: RawByteString): Boolean;
  155. begin
  156. RenameFile:=gemdos_frename(0,PAnsiChar(oldname),PAnsiChar(newname)) >= 0;
  157. end;
  158. (****** end of non portable routines ******)
  159. function FileAge (const FileName : RawByteString): Int64;
  160. var
  161. f: THandle;
  162. begin
  163. FileAge:=-1;
  164. f:=FileOpen(FileName,fmOpenRead);
  165. if f < 0 then
  166. exit;
  167. FileAge:=FileGetDate(f);
  168. FileClose(f);
  169. end;
  170. function FileGetSymLinkTarget(const FileName: RawByteString; out SymLinkRec: TRawbyteSymLinkRec): Boolean;
  171. begin
  172. Result := False;
  173. end;
  174. function FileExists (const FileName : RawByteString; FollowLink : Boolean) : Boolean;
  175. var
  176. Attr: longint;
  177. begin
  178. FileExists:=false;
  179. Attr:=FileGetAttr(FileName);
  180. if Attr < 0 then
  181. exit;
  182. result:=(Attr and (faVolumeID or faDirectory)) = 0;
  183. end;
  184. type
  185. PInternalFindData = ^TInternalFindData;
  186. TInternalFindData = record
  187. dta_original: pointer;
  188. dta_search: TDTA;
  189. end;
  190. Function InternalFindFirst (Const Path : RawByteString; Attr : Longint; out Rslt : TAbstractSearchRec; var Name: RawByteString) : Longint;
  191. var
  192. dosResult: longint;
  193. IFD: PInternalFindData;
  194. begin
  195. result:=-1; { We emulate Linux/Unix behaviour, and return -1 on errors. }
  196. new(IFD);
  197. IFD^.dta_original:=gemdos_getdta;
  198. gemdos_setdta(@IFD^.dta_search);
  199. Rslt.FindHandle:=nil;
  200. dosResult:=gemdos_fsfirst(PAnsiChar(path), Attr and faAnyFile);
  201. if dosResult < 0 then
  202. begin
  203. InternalFindClose(IFD);
  204. exit;
  205. end;
  206. Rslt.FindHandle:=IFD;
  207. with IFD^.dta_search do
  208. begin
  209. Name:=d_fname;
  210. SetCodePage(Name,DefaultFileSystemCodePage,false);
  211. Rslt.Time:=(d_date shl 16) or d_time;
  212. Rslt.Size:=d_length;
  213. { "128" is Windows "NORMALFILE" attribute. Some buggy code depend on this... :( (KB) }
  214. Rslt.Attr := 128 or d_attrib;
  215. end;
  216. result:=0;
  217. end;
  218. Function InternalFindNext (var Rslt : TAbstractSearchRec; var Name : RawByteString) : Longint;
  219. var
  220. dosResult: longint;
  221. IFD: PInternalFindData;
  222. begin
  223. result:=-1;
  224. IFD:=PInternalFindData(Rslt.FindHandle);
  225. if not assigned(IFD) then
  226. exit;
  227. dosResult:=gemdos_fsnext;
  228. if dosResult < 0 then
  229. exit;
  230. with IFD^.dta_search do
  231. begin
  232. Name:=d_fname;
  233. SetCodePage(Name,DefaultFileSystemCodePage,false);
  234. Rslt.Time:=(d_date shl 16) or d_time;
  235. Rslt.Size:=d_length;
  236. { "128" is Windows "NORMALFILE" attribute. Some buggy code depend on this... :( (KB) }
  237. Rslt.Attr := 128 or d_attrib;
  238. end;
  239. result:=0;
  240. end;
  241. Procedure InternalFindClose(var Handle: Pointer);
  242. var
  243. IFD: PInternalFindData;
  244. begin
  245. IFD:=PInternalFindData(Handle);
  246. if not assigned(IFD) then
  247. exit;
  248. gemdos_setdta(IFD^.dta_original);
  249. dispose(IFD);
  250. IFD:=nil;
  251. end;
  252. (****** end of non portable routines ******)
  253. Function FileGetAttr (Const FileName : RawByteString) : Longint;
  254. begin
  255. FileGetAttr:=gemdos_fattrib(PAnsiChar(FileName),0,0);
  256. end;
  257. Function FileSetAttr (Const Filename : RawByteString; Attr: longint) : Longint;
  258. begin
  259. FileSetAttr:=gemdos_fattrib(PAnsiChar(FileName),1,Attr and faAnyFile);
  260. if FileSetAttr < -1 then
  261. FileSetAttr:=-1
  262. else
  263. FileSetAttr:=0;
  264. end;
  265. {****************************************************************************
  266. Disk Functions
  267. ****************************************************************************}
  268. function DiskSize(Drive: Byte): Int64;
  269. var
  270. dosResult: longint;
  271. di: TDISKINFO;
  272. begin
  273. DiskSize := -1;
  274. dosResult:=gemdos_dfree(@di,drive);
  275. if dosResult < 0 then
  276. exit;
  277. DiskSize:=di.b_total * di.b_secsiz * di.b_clsiz;
  278. end;
  279. function DiskFree(Drive: Byte): Int64;
  280. var
  281. dosResult: longint;
  282. di: TDISKINFO;
  283. begin
  284. DiskFree := -1;
  285. dosResult:=gemdos_dfree(@di,drive);
  286. if dosResult < 0 then
  287. exit;
  288. DiskFree:=di.b_free * di.b_secsiz * di.b_clsiz;
  289. end;
  290. function DirectoryExists(const Directory: RawByteString; FollowLink : Boolean): Boolean;
  291. var
  292. Attr: longint;
  293. begin
  294. DirectoryExists:=false;
  295. Attr:=FileGetAttr(Directory);
  296. if Attr < 0 then
  297. exit;
  298. result:=(Attr and faDirectory) <> 0;
  299. end;
  300. {****************************************************************************
  301. Locale Functions
  302. ****************************************************************************}
  303. Procedure GetLocalTime(var SystemTime: TSystemTime);
  304. var
  305. TOSTime: Longint;
  306. begin
  307. LongRec(TOSTime).hi:=gemdos_tgetdate;
  308. LongRec(TOSTime).lo:=gemdos_tgettime;
  309. DateTimeToSystemTime(FileDateToDateTime(TOSTime),SystemTime);
  310. end;
  311. Procedure InitAnsi;
  312. Var
  313. i : longint;
  314. begin
  315. { Fill table entries 0 to 127 }
  316. for i := 0 to 96 do
  317. UpperCaseTable[i] := chr(i);
  318. for i := 97 to 122 do
  319. UpperCaseTable[i] := chr(i - 32);
  320. for i := 123 to 191 do
  321. UpperCaseTable[i] := chr(i);
  322. Move (CPISO88591UCT,UpperCaseTable[192],SizeOf(CPISO88591UCT));
  323. for i := 0 to 64 do
  324. LowerCaseTable[i] := chr(i);
  325. for i := 65 to 90 do
  326. LowerCaseTable[i] := chr(i + 32);
  327. for i := 91 to 191 do
  328. LowerCaseTable[i] := chr(i);
  329. Move (CPISO88591LCT,UpperCaseTable[192],SizeOf(CPISO88591UCT));
  330. end;
  331. Procedure InitInternational;
  332. begin
  333. InitInternationalGeneric;
  334. InitAnsi;
  335. end;
  336. function SysErrorMessage(ErrorCode: Integer): String;
  337. begin
  338. Result:=Format(SUnknownErrorCode,[ErrorCode]);
  339. end;
  340. function GetLastOSError: Integer;
  341. begin
  342. result:=-1;
  343. end;
  344. {****************************************************************************
  345. OS utility functions
  346. ****************************************************************************}
  347. function fpGetEnv(const envvar : ShortString): RawByteString; external name '_fpc_atari_getenv';
  348. function GetPathString: String;
  349. begin
  350. {writeln('Unimplemented GetPathString');}
  351. result := '';
  352. end;
  353. Function GetEnvironmentVariable(Const EnvVar : String) : String;
  354. begin
  355. GetEnvironmentVariable := fpgetenv(envvar);
  356. end;
  357. Function GetEnvironmentVariableCount : Integer;
  358. var
  359. hp : PAnsiChar;
  360. begin
  361. result:=0;
  362. hp:=basepage^.p_env;
  363. If (Hp<>Nil) then
  364. while hp^<>#0 do
  365. begin
  366. Inc(Result);
  367. hp:=hp+strlen(hp)+1;
  368. end;
  369. end;
  370. Function GetEnvironmentString(Index : Integer) : {$ifdef FPC_RTL_UNICODE}UnicodeString{$else}AnsiString{$endif};
  371. var
  372. hp : PAnsiChar;
  373. begin
  374. result:='';
  375. hp:=basepage^.p_env;
  376. If (Hp<>Nil) then
  377. begin
  378. while (hp^<>#0) and (Index>1) do
  379. begin
  380. Dec(Index);
  381. hp:=hp+strlen(hp)+1;
  382. end;
  383. If (hp^<>#0) then
  384. begin
  385. Result:=hp;
  386. end;
  387. end;
  388. end;
  389. function ExecuteProcess (const Path: RawByteString; const ComLine: RawByteString;Flags:TExecuteFlags=[]):
  390. integer;
  391. var
  392. tmpPath: RawByteString;
  393. pcmdline: ShortString;
  394. CommandLine: RawByteString;
  395. E: EOSError;
  396. env, s: PAnsiChar;
  397. buf, start: PAnsiChar;
  398. enlen, len: SizeInt;
  399. hp : PAnsiChar;
  400. begin
  401. tmpPath:=ToSingleByteFileSystemEncodedFileName(Path);
  402. pcmdline:=ToSingleByteFileSystemEncodedFileName(ComLine);
  403. { count up space needed for environment }
  404. enlen := 0;
  405. hp:=basepage^.p_env;
  406. If (Hp<>Nil) then
  407. while hp^<>#0 do
  408. begin
  409. len := strlen(hp) + 1;
  410. inc(enlen, len);
  411. inc(hp, len);
  412. end;
  413. { count up space needed for arguments }
  414. len := strlen(PAnsiChar(tmpPath)) + 1;
  415. inc(enlen, len);
  416. buf := PAnsiChar(ComLine);
  417. while (buf^<>#0) do // count nr of args
  418. begin
  419. while (buf^ in [' ',#9,#10]) do // Kill separators.
  420. inc(buf);
  421. if buf^=#0 Then
  422. break;
  423. if buf^='"' Then // quotes argument?
  424. begin
  425. inc(buf);
  426. start := buf;
  427. while not (buf^ in [#0,'"']) do // then end of argument is end of string or next quote
  428. inc(buf);
  429. len := buf - start;
  430. if len=0 then len := 1; (* TODO: needs to set NULL environment variable *)
  431. inc(len);
  432. inc(enlen, len);
  433. if buf^='"' then // skip closing quote.
  434. inc(buf);
  435. end
  436. else
  437. begin // else std
  438. start := buf;
  439. while not (buf^ in [' ',#0,#9,#10]) do
  440. inc(buf);
  441. len := buf - start + 1;
  442. inc(enlen, len);
  443. end;
  444. end;
  445. inc(enlen, 64); { filler for stuff like ARGV= and zeros }
  446. env := gemdos_malloc(enlen);
  447. if env = nil then
  448. result := ENSMEM
  449. else
  450. begin
  451. s := env;
  452. { copy the environment }
  453. hp:=basepage^.p_env;
  454. If (Hp<>Nil) then
  455. while hp^<>#0 do
  456. begin
  457. len := strlen(hp) + 1;
  458. strcopy(s, hp);
  459. inc(hp, len);
  460. inc(s, len);
  461. end;
  462. { start of arguments }
  463. strcopy(s, 'ARGV=');
  464. inc(s, 6); { s+=sizeof("ARGV=") }
  465. { copy argv[0] }
  466. buf := PAnsiChar(tmpPath);
  467. len := strlen(buf) + 1;
  468. strcopy(s, buf);
  469. inc(s, len);
  470. { copy the parameters }
  471. buf:=PAnsiChar(ComLine);
  472. while (buf^<>#0) do
  473. begin
  474. while (buf^ in [' ',#9,#10]) do // Kill separators.
  475. inc(buf);
  476. if buf^=#0 Then
  477. break;
  478. if buf^='"' Then // quotes argument?
  479. begin
  480. inc(buf);
  481. start := buf;
  482. while not (buf^ in [#0,'"']) do // then end of argument is end of string or next quote
  483. begin
  484. s^ := buf^;
  485. inc(s);
  486. inc(buf);
  487. end;
  488. if buf = start then
  489. begin
  490. s^ := ' ';
  491. inc(s);
  492. end;
  493. if buf^='"' then // skip closing quote.
  494. inc(buf);
  495. s^ := #0;
  496. inc(s);
  497. end
  498. else
  499. begin
  500. start := buf;
  501. while not (buf^ in [' ',#0,#9,#10]) do
  502. begin
  503. s^ := buf^;
  504. inc(s);
  505. inc(buf);
  506. end;
  507. s^ := #0;
  508. inc(s);
  509. end;
  510. end;
  511. { tie off environment }
  512. s^ := #0;
  513. inc(s);
  514. s^ := #0;
  515. { signal Extended Argument Passing }
  516. pcmdline[0] := #127;
  517. { the zero offset for cmdline is actually correct here. pexec() expects
  518. pascal formatted string for cmdline, so length in first byte }
  519. result:=gemdos_pexec(0,PAnsiChar(tmpPath),@pcmdline[0],env);
  520. gemdos_mfree(env);
  521. end;
  522. if result < 0 then begin
  523. if ComLine = '' then
  524. CommandLine := Path
  525. else
  526. CommandLine := Path + ' ' + ComLine;
  527. E := EOSError.CreateFmt (SExecuteProcessFailed, [CommandLine, result]);
  528. E.ErrorCode := result;
  529. raise E;
  530. end;
  531. end;
  532. function ExecuteProcess (const Path: RawByteString;
  533. const ComLine: array of RawByteString;Flags:TExecuteFlags=[]): integer;
  534. var
  535. CommandLine: RawByteString;
  536. I: integer;
  537. begin
  538. Commandline := '';
  539. for I := 0 to High (ComLine) do
  540. if Pos (' ', ComLine [I]) <> 0 then
  541. CommandLine := CommandLine + ' ' + '"' + ToSingleByteFileSystemEncodedFileName(ComLine [I]) + '"'
  542. else
  543. CommandLine := CommandLine + ' ' + ToSingleByteFileSystemEncodedFileName(Comline [I]);
  544. ExecuteProcess := ExecuteProcess (Path, CommandLine);
  545. end;
  546. procedure Sleep(Milliseconds: cardinal);
  547. begin
  548. {writeln('Unimplemented Sleep');}
  549. end;
  550. {****************************************************************************
  551. Initialization code
  552. ****************************************************************************}
  553. Initialization
  554. InitExceptions;
  555. InitInternational; { Initialize internationalization settings }
  556. OnBeep:=Nil; { No SysBeep() on Atari for now. }
  557. Finalization
  558. FreeTerminateProcs;
  559. DoneExceptions;
  560. end.