dos.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. {****************************************************************************
  2. Free Pascal Runtime-Library
  3. DOS unit for OS/2
  4. Copyright (c) 1997,1999-2000 by Daniel Mantione,
  5. member of the Free Pascal development team
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. ****************************************************************************}
  12. unit dos;
  13. {$ASMMODE ATT}
  14. {***************************************************************************}
  15. interface
  16. {***************************************************************************}
  17. {$PACKRECORDS 1}
  18. uses Strings, DosCalls;
  19. Type
  20. {Search record which is used by findfirst and findnext:}
  21. SearchRec = record
  22. case boolean of
  23. false: (Handle: THandle; {Used in os_OS2 mode}
  24. FStat: PFileFindBuf3;
  25. Fill: array [1..21 - SizeOf (THandle) - SizeOf (pointer)]
  26. of byte;
  27. Attr: byte;
  28. Time: longint;
  29. Size: longint;
  30. Name: string); {Filenames can be long in OS/2!}
  31. true: (Fill2: array [1..21] of byte;
  32. Attr2: byte;
  33. Time2: longint;
  34. Size2: longint;
  35. Name2: string); {Filenames can be long in OS/2!}
  36. end;
  37. {Flags for the exec procedure:
  38. }
  39. threadvar
  40. (* For compatibility with VP/2, used for runflags in Exec procedure. *)
  41. ExecFlags: cardinal;
  42. (* Note that the TP/BP compatible method for retrieval of exit codes *)
  43. (* is limited to only one (the last) execution! Including the following *)
  44. (* two variables in the interface part allows querying the status of *)
  45. (* of asynchronously started programs using DosWaitChild with dtNoWait *)
  46. (* parameter, i.e. without waiting for the final program result (as *)
  47. (* opposed to calling DosExitCode which would wait for the exit code). *)
  48. LastExecRes: TResultCodes;
  49. LastExecFlags: cardinal;
  50. {$i dosh.inc}
  51. {OS/2 specific functions}
  52. function GetEnvPChar (EnvVar: string): PChar;
  53. function DosErrorModuleName: string;
  54. (* In case of an error in Dos.Exec returns the name of the module *)
  55. (* causing the problem - e.g. name of a missing or corrupted DLL. *)
  56. (* It may also contain a queue name in case of a failed attempt *)
  57. (* to create queue for reading results of started sessions. *)
  58. implementation
  59. {$DEFINE HAS_GETMSCOUNT}
  60. {$DEFINE HAS_DOSEXITCODE}
  61. {$DEFINE FPC_FEXPAND_UNC} (* UNC paths are supported *)
  62. {$DEFINE FPC_FEXPAND_DRIVES} (* Full paths begin with drive specification *)
  63. {$DEFINE FPC_FEXPAND_GETENV_PCHAR}
  64. {$I dos.inc}
  65. threadvar
  66. LastDosErrorModuleName: string;
  67. const FindResvdMask = $00003737; {Allowed bits in attribute
  68. specification for DosFindFirst call.}
  69. function GetMsCount: int64;
  70. var
  71. L: cardinal;
  72. begin
  73. DosQuerySysInfo (svMsCount, svMsCount, L, 4);
  74. GetMsCount := L;
  75. end;
  76. function fsearch(path:pathstr;dirlist:string):pathstr;
  77. Var
  78. A: array [0..255] of char;
  79. D, P: AnsiString;
  80. begin
  81. P:=Path;
  82. D:=DirList;
  83. DosError := DosSearchPath (dsIgnoreNetErrs, PChar(D), PChar(P), @A, 255);
  84. fsearch := StrPas (@A);
  85. end;
  86. procedure getftime(var f;var time:longint);
  87. var
  88. FStat: TFileStatus3;
  89. begin
  90. DosError := DosQueryFileInfo (FileRec (F).Handle, ilStandard, @FStat,
  91. SizeOf (FStat));
  92. if DosError=0 then
  93. begin
  94. Time := FStat.TimeLastWrite + longint (FStat.DateLastWrite) shl 16;
  95. if Time = 0 then
  96. Time := FStat.TimeCreation + longint (FStat.DateCreation) shl 16;
  97. end else
  98. Time:=0;
  99. end;
  100. procedure SetFTime (var F; Time: longint);
  101. var FStat: TFileStatus3;
  102. RC: cardinal;
  103. begin
  104. RC := DosQueryFileInfo (FileRec (F).Handle, ilStandard, @FStat,
  105. SizeOf (FStat));
  106. if RC = 0 then
  107. begin
  108. FStat.DateLastAccess := Hi (Time);
  109. FStat.DateLastWrite := Hi (Time);
  110. FStat.TimeLastAccess := Lo (Time);
  111. FStat.TimeLastWrite := Lo (Time);
  112. RC := DosSetFileInfo (FileRec (F).Handle, ilStandard, @FStat,
  113. SizeOf (FStat));
  114. end;
  115. DosError := integer (RC);
  116. end;
  117. function DosExitCode: word;
  118. var
  119. Res: TResultCodes;
  120. PPID: cardinal;
  121. RC: cardinal;
  122. begin
  123. if (LastExecFlags = deAsyncResult) or (LastExecFlags = deAsyncResultDb) then
  124. begin
  125. RC := DosWaitChild (DCWA_PROCESS, dtWait, Res, PPID, LastExecRes.PID);
  126. if RC = 0 then
  127. (* If we succeeded, the process is finished - possible future querying
  128. of DosExitCode shall return the result immediately as with synchronous
  129. execution. *)
  130. begin
  131. LastExecFlags := deSync;
  132. LastExecRes := Res;
  133. end
  134. else
  135. LastExecRes.ExitCode := RC shl 16;
  136. end;
  137. if LastExecRes.ExitCode > high (word) then
  138. DosExitCode := high (word)
  139. else
  140. DosExitCode := LastExecRes.ExitCode and $FFFF;
  141. end;
  142. procedure Exec (const Path: PathStr; const ComLine: ComStr);
  143. {Execute a program.}
  144. var
  145. Args0, Args: PByteArray;
  146. ArgSize: word;
  147. ObjName: string;
  148. Res: TResultCodes;
  149. RC: cardinal;
  150. ExecAppType: cardinal;
  151. HQ: THandle;
  152. SPID, STID, SCtr, QName: string;
  153. SID, PID: cardinal;
  154. SD: TStartData;
  155. RD: TRequestData;
  156. PCI: PChildInfo;
  157. CISize: cardinal;
  158. Prio: byte;
  159. DSS: boolean;
  160. SR: SearchRec;
  161. const
  162. MaxArgsSize = 3072; (* Amount of memory reserved for arguments in bytes. *)
  163. begin
  164. { LastDosExitCode := Exec (Path, ExecRunFlags (ExecFlags), efDefault, ComLine);}
  165. ObjName := '';
  166. (* FExpand should be used only for the DosStartSession part
  167. and only if the executable is in the current directory. *)
  168. FindFirst (Path, AnyFile, SR);
  169. if DosError = 0 then
  170. QName := FExpand (Path)
  171. else
  172. QName := Path;
  173. FindClose (SR);
  174. if ComLine = '' then
  175. begin
  176. Args0 := nil;
  177. Args := nil;
  178. end
  179. else
  180. begin
  181. GetMem (Args0, MaxArgsSize);
  182. Args := Args0;
  183. (* Work around a bug in OS/2 - argument to DosExecPgm *)
  184. (* should not cross a 64K boundary. *)
  185. if ((PtrUInt (Args) + 1024) and $FFFF) < 1024 then
  186. Inc (pointer (Args), 1024);
  187. ArgSize := 0;
  188. Move (QName [1], Args^ [ArgSize], Length (QName));
  189. Inc (ArgSize, Length (QName));
  190. Args^ [ArgSize] := 0;
  191. Inc (ArgSize);
  192. {Now do the real arguments.}
  193. Move (ComLine [1], Args^ [ArgSize], Length (ComLine));
  194. Inc (ArgSize, Length (ComLine));
  195. Args^ [ArgSize] := 0;
  196. Inc (ArgSize);
  197. Args^ [ArgSize] := 0;
  198. end;
  199. if (DosQueryAppType (PChar (Args), ExecAppType) = 0) and
  200. (ApplicationType and 3 = ExecAppType and 3) then
  201. (* DosExecPgm should work... *)
  202. begin
  203. DSS := false;
  204. Res.ExitCode := $FFFFFFFF;
  205. RC := DosExecPgm (ObjName, cardinal (ExecFlags), Args, nil, Res, Path);
  206. if RC = 0 then
  207. begin
  208. LastExecFlags := ExecFlags;
  209. LastExecRes := Res;
  210. LastDosErrorModuleName := '';
  211. end
  212. else
  213. if (RC = 190) or (RC = 191) then
  214. DSS := true;
  215. end
  216. else
  217. DSS := true;
  218. if DSS then
  219. begin
  220. Str (GetProcessID, SPID);
  221. Str (ThreadID, STID);
  222. QName := '\QUEUES\FPC_Dos_Exec_p' + SPID + 't' + STID + '.QUE'#0;
  223. FillChar (SD, SizeOf (SD), 0);
  224. SD.Length := SizeOf (SD);
  225. RC := 0;
  226. case ExecFlags of
  227. deSync:
  228. begin
  229. SD.Related := ssf_Related_Child;
  230. LastExecFlags := ExecFlags;
  231. SD.TermQ := @QName [1];
  232. RC := DosCreateQueue (HQ, quFIFO or quConvert_Address, @QName [1]);
  233. end;
  234. deAsync,
  235. deAsyncResult:
  236. begin
  237. (* Current implementation of DosExitCode does not support retrieval *)
  238. (* of result codes for other session types started asynchronously. *)
  239. LastExecFlags := deAsync;
  240. SD.Related := ssf_Related_Independent;
  241. end;
  242. deBackground:
  243. begin
  244. (* Current implementation of DosExitCode does not support retrieval *)
  245. (* of result codes for other session types started asynchronously. *)
  246. LastExecFlags := ExecFlags;
  247. SD.Related := ssf_Related_Independent;
  248. SD.FgBg := ssf_FgBg_Back;
  249. end;
  250. deAsyncResultDB:
  251. begin
  252. (* Current implementation of DosExitCode does not support retrieval *)
  253. (* of result codes for other session types started asynchronously. *)
  254. LastExecFlags := ExecFlags;
  255. SD.Related := ssf_Related_Child;
  256. SD.TraceOpt := ssf_TraceOpt_Trace;
  257. end;
  258. end;
  259. if RC <> 0 then
  260. ObjName := Copy (QName, 1, Pred (Length (QName)))
  261. else
  262. begin
  263. if Args = nil then
  264. (* No parameters passed, Args not allocated for DosExecPgm, so allocate now. *)
  265. begin
  266. GetMem (Args0, MaxArgsSize);
  267. Args := Args0;
  268. Move (QName [1], Args^ [0], Length (QName));
  269. Args^ [Length (QName)] := 0;
  270. end
  271. else
  272. SD.PgmInputs := PChar (@Args^ [Length (QName) + 1]);
  273. SD.PgmName := PChar (Args);
  274. SD.InheritOpt := ssf_InhertOpt_Parent;
  275. SD.ObjectBuffer := @ObjName [1];
  276. SD.ObjectBuffLen := SizeOf (ObjName) - 1;
  277. RC := DosStartSession (SD, SID, PID);
  278. if (RC = 0) or (RC = 457) then
  279. begin
  280. LastExecRes.PID := PID;
  281. if ExecFlags = deSync then
  282. begin
  283. RC := DosReadQueue (HQ, RD, CISize, PCI, 0, 0, Prio, 0);
  284. if (RC = 0) and (PCI^.SessionID = SID) then
  285. begin
  286. LastExecRes.ExitCode := PCI^.Return;
  287. DosCloseQueue (HQ);
  288. DosFreeMem (PCI);
  289. end
  290. else
  291. DosCloseQueue (HQ);
  292. end;
  293. end
  294. else if ExecFlags = deSync then
  295. DosCloseQueue (HQ);
  296. end;
  297. end;
  298. if RC <> 0 then
  299. begin
  300. LastDosErrorModuleName := ObjName;
  301. LastExecFlags := deSync;
  302. LastExecRes.ExitCode := 0; (* Needed for TP/BP compatibility *)
  303. LastExecRes.TerminateReason := $FFFFFFFF;
  304. end;
  305. DosError := RC;
  306. if Args0 <> nil then
  307. FreeMem (Args0, MaxArgsSize);
  308. end;
  309. function DosErrorModuleName: string;
  310. begin
  311. DosErrorModuleName := LastDosErrorModuleName;
  312. end;
  313. function dosversion:word;
  314. {Returns OS/2 version}
  315. var
  316. Minor, Major: Cardinal;
  317. begin
  318. DosQuerySysInfo(svMajorVersion, svMajorVersion, Major, 4);
  319. DosQuerySysInfo(svMinorVersion, svMinorVersion, Minor, 4);
  320. DosVersion:=Major or Minor shl 8;
  321. end;
  322. procedure GetDate (var Year, Month, MDay, WDay: word);
  323. Var
  324. dt: TDateTime;
  325. begin
  326. DosGetDateTime(dt);
  327. Year:=dt.year;
  328. Month:=dt.month;
  329. MDay:=dt.Day;
  330. WDay:=dt.Weekday;
  331. end;
  332. procedure SetDate (Year, Month, Day: word);
  333. var
  334. DT: TDateTime;
  335. begin
  336. DosGetDateTime (DT);
  337. DT.Year := Year;
  338. DT.Month := byte (Month);
  339. DT.Day := byte (Day);
  340. DosSetDateTime (DT);
  341. end;
  342. procedure GetTime (var Hour, Minute, Second, Sec100: word);
  343. var
  344. dt: TDateTime;
  345. begin
  346. DosGetDateTime(dt);
  347. Hour:=dt.Hour;
  348. Minute:=dt.Minute;
  349. Second:=dt.Second;
  350. Sec100:=dt.Hundredths;
  351. end;
  352. procedure SetTime (Hour, Minute, Second, Sec100: word);
  353. var
  354. DT: TDateTime;
  355. begin
  356. DosGetDateTime (DT);
  357. DT.Hour := byte (Hour);
  358. DT.Minute := byte (Minute);
  359. DT.Second := byte (Second);
  360. DT.Sec100 := byte (Sec100);
  361. DosSetDateTime (DT);
  362. end;
  363. function DiskFree (Drive: byte): int64;
  364. var FI: TFSinfo;
  365. RC: cardinal;
  366. begin
  367. {In OS/2, we use the filesystem information.}
  368. RC := DosQueryFSInfo (Drive, 1, FI, SizeOf (FI));
  369. if RC = 0 then
  370. DiskFree := int64 (FI.Free_Clusters) *
  371. int64 (FI.Sectors_Per_Cluster) * int64 (FI.Bytes_Per_Sector)
  372. else
  373. DiskFree := -1;
  374. end;
  375. function DiskSize (Drive: byte): int64;
  376. var FI: TFSinfo;
  377. RC: cardinal;
  378. begin
  379. RC := DosQueryFSinfo (Drive, 1, FI, SizeOf (FI));
  380. if RC = 0 then
  381. DiskSize := int64 (FI.Total_Clusters) *
  382. int64 (FI.Sectors_Per_Cluster) * int64 (FI.Bytes_Per_Sector)
  383. else
  384. DiskSize := -1;
  385. end;
  386. procedure DosSearchRec2SearchRec (var F: SearchRec);
  387. type
  388. TRec = record
  389. T, D: word;
  390. end;
  391. begin
  392. with F do
  393. begin
  394. Name := FStat^.Name;
  395. Size := FStat^.FileSize;
  396. Attr := byte(FStat^.AttrFile and $FF);
  397. TRec (Time).T := FStat^.TimeLastWrite;
  398. TRec (Time).D := FStat^.DateLastWrite;
  399. end;
  400. end;
  401. procedure FindFirst (const Path: PathStr; Attr: word; var F: SearchRec);
  402. var Count: cardinal;
  403. begin
  404. {No error.}
  405. DosError := 0;
  406. New (F.FStat);
  407. F.Handle := THandle ($FFFFFFFF);
  408. Count := 1;
  409. DosError := integer (DosFindFirst (Path, F.Handle,
  410. Attr and FindResvdMask, F.FStat, SizeOf (F.FStat^),
  411. Count, ilStandard));
  412. if (DosError = 0) and (Count = 0) then DosError := 18;
  413. DosSearchRec2SearchRec (F);
  414. end;
  415. procedure FindNext (var F: SearchRec);
  416. var
  417. Count: cardinal;
  418. begin
  419. {No error}
  420. DosError := 0;
  421. Count := 1;
  422. DosError := integer (DosFindNext (F.Handle, F.FStat, SizeOf (F.FStat^),
  423. Count));
  424. if (DosError = 0) and (Count = 0) then DosError := 18;
  425. DosSearchRec2SearchRec (F);
  426. end;
  427. procedure FindClose (var F: SearchRec);
  428. begin
  429. if F.Handle <> THandle ($FFFFFFFF) then DosError := DosFindClose (F.Handle);
  430. Dispose (F.FStat);
  431. end;
  432. function envcount:longint;
  433. begin
  434. envcount:=envc;
  435. end;
  436. function envstr (index : longint) : string;
  437. var hp:Pchar;
  438. begin
  439. if (index<=0) or (index>envcount) then
  440. begin
  441. envstr:='';
  442. exit;
  443. end;
  444. hp:=EnvP[index-1];
  445. envstr:=strpas(hp);
  446. end;
  447. function GetEnvPChar (EnvVar: string): PChar;
  448. (* The assembler version is more than three times as fast as Pascal. *)
  449. var
  450. P: PChar;
  451. begin
  452. EnvVar := UpCase (EnvVar);
  453. {$ASMMODE INTEL}
  454. asm
  455. cld
  456. mov edi, Environment
  457. lea esi, EnvVar
  458. xor eax, eax
  459. lodsb
  460. @NewVar:
  461. cmp byte ptr [edi], 0
  462. jz @Stop
  463. push eax { eax contains length of searched variable name }
  464. push esi { esi points to the beginning of the variable name }
  465. mov ecx, -1 { our character ('=' - see below) _must_ be found }
  466. mov edx, edi { pointer to beginning of variable name saved in edx }
  467. mov al, '=' { searching until '=' (end of variable name) }
  468. repne
  469. scasb { scan until '=' not found }
  470. neg ecx { what was the name length? }
  471. dec ecx { corrected }
  472. dec ecx { exclude the '=' character }
  473. pop esi { restore pointer to beginning of variable name }
  474. pop eax { restore length of searched variable name }
  475. push eax { and save both of them again for later use }
  476. push esi
  477. cmp ecx, eax { compare length of searched variable name with name }
  478. jnz @NotEqual { ... of currently found variable, jump if different }
  479. xchg edx, edi { pointer to current variable name restored in edi }
  480. repe
  481. cmpsb { compare till the end of variable name }
  482. xchg edx, edi { pointer to beginning of variable contents in edi }
  483. jz @Equal { finish if they're equal }
  484. @NotEqual:
  485. xor eax, eax { look for 00h }
  486. mov ecx, -1 { it _must_ be found }
  487. repne
  488. scasb { scan until found }
  489. pop esi { restore pointer to beginning of variable name }
  490. pop eax { restore length of searched variable name }
  491. jmp @NewVar { ... or continue with new variable otherwise }
  492. @Stop:
  493. xor eax, eax
  494. mov P, eax { Not found - return nil }
  495. jmp @End
  496. @Equal:
  497. pop esi { restore the stack position }
  498. pop eax
  499. mov P, edi { place pointer to variable contents in P }
  500. @End:
  501. end ['eax','ecx','edx','esi','edi'];
  502. GetEnvPChar := P;
  503. end;
  504. {$ASMMODE ATT}
  505. Function GetEnv(envvar: string): string;
  506. (* The assembler version is more than three times as fast as Pascal. *)
  507. begin
  508. GetEnv := StrPas (GetEnvPChar (EnvVar));
  509. end;
  510. procedure GetFAttr (var F; var Attr: word);
  511. var
  512. PathInfo: TFileStatus3;
  513. RC: cardinal;
  514. begin
  515. Attr := 0;
  516. RC := DosQueryPathInfo (@FileRec (F).Name, ilStandard,
  517. @PathInfo, SizeOf (PathInfo));
  518. DosError := integer (RC);
  519. if RC = 0 then
  520. Attr := PathInfo.AttrFile;
  521. end;
  522. procedure SetFAttr (var F; Attr: word);
  523. var
  524. PathInfo: TFileStatus3;
  525. RC: cardinal;
  526. begin
  527. RC := DosQueryPathInfo (@FileRec (F).Name, ilStandard,
  528. @PathInfo, SizeOf (PathInfo));
  529. if RC = 0 then
  530. begin
  531. PathInfo.AttrFile := Attr;
  532. RC := DosSetPathInfo (@FileRec (F).Name, ilStandard, @PathInfo,
  533. SizeOf (PathInfo), doWriteThru);
  534. end;
  535. DosError := integer (RC);
  536. end;
  537. {function GetShortName(var p : String) : boolean;
  538. begin
  539. GetShortName:=true;}
  540. {$WARNING EA .shortname support (see FAT32 driver) should be probably added here!}
  541. {end;
  542. function GetLongName(var p : String) : boolean;
  543. begin
  544. GetLongName:=true;}
  545. {$WARNING EA .longname support should be probably added here!}
  546. {end;}
  547. begin
  548. FillChar (LastExecRes, SizeOf (LastExecRes), 0);
  549. LastDosErrorModuleName := '';
  550. ExecFlags := 0;
  551. LastExecFlags := deSync;
  552. end.