sysutils.pp 16 KB

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