sysutils.pp 16 KB

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