dos.pp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2014 by the Free Pascal development team
  4. DOS unit for AmigaOS & clones
  5. Heavily based on the 1.x Amiga version by Nils Sjoholm and
  6. Carl Eric Codere
  7. AmigaOS and MorphOS support by Karoly Balogh
  8. AROS support by Marcus Sackrow
  9. See the file COPYING.FPC, included in this distribution,
  10. for details about the copyright.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. **********************************************************************}
  15. {$INLINE ON}
  16. unit Dos;
  17. {--------------------------------------------------------------------}
  18. { LEFT TO DO: }
  19. {--------------------------------------------------------------------}
  20. { o DiskFree / Disksize don't work as expected }
  21. { o Implement EnvCount,EnvStr }
  22. { o FindFirst should only work with correct attributes }
  23. {--------------------------------------------------------------------}
  24. interface
  25. type
  26. SearchRec = Packed Record
  27. { watch out this is correctly aligned for all processors }
  28. { don't modify. }
  29. { Replacement for Fill }
  30. {0} AnchorPtr : Pointer; { Pointer to the Anchorpath structure }
  31. {4} Fill: Array[1..15] of Byte; {future use}
  32. {End of replacement for fill}
  33. Attr : BYTE; {attribute of found file}
  34. Time : LongInt; {last modify date of found file}
  35. Size : LongInt; {file size of found file}
  36. Name : String[255]; {name of found file}
  37. End;
  38. {$I dosh.inc}
  39. function DeviceByIdx(Idx: Integer): string;
  40. function AddDisk(Const Path: string): Integer;
  41. function RefreshDeviceList: Integer;
  42. function DiskSize(Drive: AnsiString): Int64;
  43. function DiskFree(Drive: AnsiString): Int64;
  44. implementation
  45. {$DEFINE HAS_GETMSCOUNT}
  46. {$DEFINE HAS_GETCBREAK}
  47. {$DEFINE HAS_SETCBREAK}
  48. {$DEFINE FPC_FEXPAND_VOLUMES} (* Full paths begin with drive specification *)
  49. {$DEFINE FPC_FEXPAND_DRIVESEP_IS_ROOT}
  50. {$DEFINE FPC_FEXPAND_NO_DEFAULT_PATHS}
  51. {$I dos.inc}
  52. { * include MorphOS specific functions & definitions * }
  53. {$include execd.inc}
  54. {$include execf.inc}
  55. {$include timerd.inc}
  56. {$include doslibd.inc}
  57. {$include doslibf.inc}
  58. {$include utilf.inc}
  59. const
  60. DaysPerMonth : Array[1..12] of ShortInt =
  61. (031,028,031,030,031,030,031,031,030,031,030,031);
  62. DaysPerYear : Array[1..12] of Integer =
  63. (031,059,090,120,151,181,212,243,273,304,334,365);
  64. DaysPerLeapYear : Array[1..12] of Integer =
  65. (031,060,091,121,152,182,213,244,274,305,335,366);
  66. SecsPerYear : LongInt = 31536000;
  67. SecsPerLeapYear : LongInt = 31622400;
  68. SecsPerDay : LongInt = 86400;
  69. SecsPerHour : Integer = 3600;
  70. SecsPerMinute : ShortInt = 60;
  71. TICKSPERSECOND = 50;
  72. {******************************************************************************
  73. --- Internal routines ---
  74. ******************************************************************************}
  75. { * PathConv is implemented in the system unit! * }
  76. function PathConv(path: string): string; external name 'PATHCONV';
  77. function dosLock(const name: String;
  78. accessmode: Longint) : LongInt;
  79. var
  80. buffer: array[0..255] of Char;
  81. begin
  82. move(name[1],buffer,length(name));
  83. buffer[length(name)]:=#0;
  84. dosLock:=Lock(buffer,accessmode);
  85. end;
  86. function BADDR(bval: LongInt): Pointer; Inline;
  87. begin
  88. {$if defined(AROS) and (not defined(AROS_FLAVOUR_BINCOMPAT))}
  89. BADDR := Pointer(bval);
  90. {$else}
  91. BADDR:=Pointer(bval Shl 2);
  92. {$endif}
  93. end;
  94. function BSTR2STRING(s : Pointer): PChar; Inline;
  95. begin
  96. {$if defined(AROS) and (not defined(AROS_FLAVOUR_BINCOMPAT))}
  97. BSTR2STRING:=PChar(s);
  98. {$else}
  99. BSTR2STRING:=PChar(BADDR(PtrInt(s)))+1;
  100. {$endif}
  101. end;
  102. function BSTR2STRING(s : LongInt): PChar; Inline;
  103. begin
  104. {$if defined(AROS) and (not defined(AROS_FLAVOUR_BINCOMPAT))}
  105. BSTR2STRING:=PChar(s);
  106. {$else}
  107. BSTR2STRING:=PChar(BADDR(s))+1;
  108. {$endif}
  109. end;
  110. function IsLeapYear(Source : Word) : Boolean;
  111. begin
  112. if (source Mod 400 = 0) or ((source Mod 4 = 0) and (source Mod 100 <> 0)) then
  113. IsLeapYear:=True
  114. else
  115. IsLeapYear:=False;
  116. end;
  117. procedure Amiga2DateStamp(Date : LongInt; var TotalDays,Minutes,Ticks: longint);
  118. { Converts a value in seconds past 1978 to a value in AMIGA DateStamp format }
  119. { Taken from SWAG and modified to work with the Amiga format - CEC }
  120. var
  121. LocalDate : LongInt;
  122. Done : Boolean;
  123. TotDays : Integer;
  124. Y: Word;
  125. H: Word;
  126. Min: Word;
  127. S : Word;
  128. begin
  129. Y := 1978; H := 0; Min := 0; S := 0;
  130. TotalDays := 0;
  131. Minutes := 0;
  132. Ticks := 0;
  133. LocalDate := Date;
  134. Done := false;
  135. while not Done do
  136. begin
  137. if LocalDate >= SecsPerYear then
  138. begin
  139. Inc(Y,1);
  140. Dec(LocalDate,SecsPerYear);
  141. Inc(TotalDays,DaysPerYear[12]);
  142. end else
  143. Done := true;
  144. if (IsLeapYear(Y+1)) and (LocalDate >= SecsPerLeapYear) and
  145. (Not Done) then
  146. begin
  147. Inc(Y,1);
  148. Dec(LocalDate,SecsPerLeapYear);
  149. Inc(TotalDays,DaysPerLeapYear[12]);
  150. end;
  151. end; { END WHILE }
  152. TotDays := LocalDate Div SecsPerDay;
  153. { Total number of days }
  154. TotalDays := TotalDays + TotDays;
  155. Dec(LocalDate,TotDays*SecsPerDay);
  156. { Absolute hours since start of day }
  157. H := LocalDate Div SecsPerHour;
  158. { Convert to minutes }
  159. Minutes := H*60;
  160. Dec(LocalDate,(H * SecsPerHour));
  161. { Find the remaining minutes to add }
  162. Min := LocalDate Div SecsPerMinute;
  163. Dec(LocalDate,(Min * SecsPerMinute));
  164. Minutes:=Minutes+Min;
  165. { Find the number of seconds and convert to ticks }
  166. S := LocalDate;
  167. Ticks:=TICKSPERSECOND*S;
  168. end;
  169. function dosSetProtection(const name: string; mask:longint): Boolean;
  170. var
  171. buffer : array[0..255] of Char;
  172. begin
  173. move(name[1],buffer,length(name));
  174. buffer[length(name)]:=#0;
  175. dosSetProtection:=SetProtection(buffer,mask) <> 0;
  176. end;
  177. function dosSetFileDate(name: string; p : PDateStamp): Boolean;
  178. var
  179. buffer : array[0..255] of Char;
  180. begin
  181. move(name[1],buffer,length(name));
  182. buffer[length(name)]:=#0;
  183. dosSetFileDate:=SetFileDate(buffer,p);
  184. end;
  185. {******************************************************************************
  186. --- Info / Date / Time ---
  187. ******************************************************************************}
  188. function DosVersion: Word;
  189. var p: PLibrary;
  190. begin
  191. p:=PLibrary(AOS_DOSBase);
  192. DosVersion:= p^.lib_Version or (p^.lib_Revision shl 8);
  193. end;
  194. { Here are a lot of stuff just for setdate and settime }
  195. var
  196. TimerBase : Pointer;
  197. procedure NewList (list: pList);
  198. begin
  199. with list^ do begin
  200. lh_Head := pNode(@lh_Tail);
  201. lh_Tail := NIL;
  202. lh_TailPred := pNode(@lh_Head)
  203. end;
  204. end;
  205. function CreateExtIO (port: pMsgPort; size: Longint): pIORequest;
  206. var
  207. IOReq: pIORequest;
  208. begin
  209. IOReq := NIL;
  210. if port <> NIL then
  211. begin
  212. IOReq := execAllocMem(size, MEMF_CLEAR);
  213. if IOReq <> NIL then
  214. begin
  215. IOReq^.io_Message.mn_Node.ln_Type := 7;
  216. IOReq^.io_Message.mn_Length := size;
  217. IOReq^.io_Message.mn_ReplyPort := port;
  218. end;
  219. end;
  220. CreateExtIO := IOReq;
  221. end;
  222. procedure DeleteExtIO (ioReq: pIORequest);
  223. begin
  224. if ioReq <> NIL then
  225. begin
  226. ioReq^.io_Message.mn_Node.ln_Type := $FF;
  227. ioReq^.io_Message.mn_ReplyPort := pMsgPort(-1);
  228. ioReq^.io_Device := pDevice(-1);
  229. execFreeMem(ioReq, ioReq^.io_Message.mn_Length);
  230. end
  231. end;
  232. function Createport(name : PChar; pri : longint): pMsgPort;
  233. var
  234. sigbit : ShortInt;
  235. port : pMsgPort;
  236. begin
  237. sigbit := AllocSignal(-1);
  238. if sigbit = -1 then CreatePort := nil;
  239. port := execAllocMem(sizeof(tMsgPort),MEMF_CLEAR);
  240. if port = nil then begin
  241. FreeSignal(sigbit);
  242. CreatePort := nil;
  243. end;
  244. with port^ do begin
  245. if assigned(name) then
  246. mp_Node.ln_Name := name
  247. else mp_Node.ln_Name := nil;
  248. mp_Node.ln_Pri := pri;
  249. mp_Node.ln_Type := 4;
  250. mp_Flags := 0;
  251. mp_SigBit := sigbit;
  252. mp_SigTask := FindTask(nil);
  253. end;
  254. if assigned(name) then AddPort(port)
  255. else NewList(addr(port^.mp_MsgList));
  256. CreatePort := port;
  257. end;
  258. procedure DeletePort (port: pMsgPort);
  259. begin
  260. if port <> NIL then
  261. begin
  262. if port^.mp_Node.ln_Name <> NIL then
  263. RemPort(port);
  264. port^.mp_Node.ln_Type := $FF;
  265. port^.mp_MsgList.lh_Head := pNode(-1);
  266. FreeSignal(port^.mp_SigBit);
  267. execFreeMem(port, sizeof(tMsgPort));
  268. end;
  269. end;
  270. function Create_Timer(theUnit : longint) : pTimeRequest;
  271. var
  272. Error : longint;
  273. TimerPort : pMsgPort;
  274. TimeReq : pTimeRequest;
  275. begin
  276. TimerPort := CreatePort(Nil, 0);
  277. if TimerPort = Nil then
  278. Create_Timer := Nil;
  279. TimeReq := pTimeRequest(CreateExtIO(TimerPort,sizeof(tTimeRequest)));
  280. if TimeReq = Nil then begin
  281. DeletePort(TimerPort);
  282. Create_Timer := Nil;
  283. end;
  284. Error := OpenDevice(TIMERNAME, theUnit, pIORequest(TimeReq), 0);
  285. if Error <> 0 then begin
  286. DeleteExtIO(pIORequest(TimeReq));
  287. DeletePort(TimerPort);
  288. Create_Timer := Nil;
  289. end;
  290. TimerBase := pointer(TimeReq^.tr_Node.io_Device);
  291. Create_Timer := pTimeRequest(TimeReq);
  292. end;
  293. Procedure Delete_Timer(WhichTimer : pTimeRequest);
  294. var
  295. WhichPort : pMsgPort;
  296. begin
  297. WhichPort := WhichTimer^.tr_Node.io_Message.mn_ReplyPort;
  298. if assigned(WhichTimer) then begin
  299. CloseDevice(pIORequest(WhichTimer));
  300. DeleteExtIO(pIORequest(WhichTimer));
  301. end;
  302. if assigned(WhichPort) then
  303. DeletePort(WhichPort);
  304. end;
  305. function set_new_time(secs, micro : longint): longint;
  306. var
  307. tr : ptimerequest;
  308. begin
  309. tr := create_timer(UNIT_MICROHZ);
  310. { non zero return says error }
  311. if tr = nil then set_new_time := -1;
  312. tr^.tr_time.tv_secs := secs;
  313. tr^.tr_time.tv_micro := micro;
  314. tr^.tr_node.io_Command := TR_SETSYSTIME;
  315. DoIO(pIORequest(tr));
  316. delete_timer(tr);
  317. set_new_time := 0;
  318. end;
  319. function get_sys_time(tv : ptimeval): longint;
  320. var
  321. tr : ptimerequest;
  322. begin
  323. tr := create_timer( UNIT_MICROHZ );
  324. { non zero return says error }
  325. if tr = nil then get_sys_time := -1;
  326. tr^.tr_node.io_Command := TR_GETSYSTIME;
  327. DoIO(pIORequest(tr));
  328. { structure assignment }
  329. tv^ := tr^.tr_time;
  330. delete_timer(tr);
  331. get_sys_time := 0;
  332. end;
  333. procedure GetDate(Var Year, Month, MDay, WDay: Word);
  334. var
  335. cd : pClockData;
  336. oldtime : ttimeval;
  337. begin
  338. new(cd);
  339. get_sys_time(@oldtime);
  340. Amiga2Date(oldtime.tv_secs,cd);
  341. Year := cd^.year;
  342. Month := cd^.month;
  343. MDay := cd^.mday;
  344. WDay := cd^.wday;
  345. dispose(cd);
  346. end;
  347. procedure SetDate(Year, Month, Day: Word);
  348. var
  349. cd : pClockData;
  350. oldtime : ttimeval;
  351. begin
  352. new(cd);
  353. get_sys_time(@oldtime);
  354. Amiga2Date(oldtime.tv_secs,cd);
  355. cd^.year := Year;
  356. cd^.month := Month;
  357. cd^.mday := Day;
  358. set_new_time(Date2Amiga(cd),0);
  359. dispose(cd);
  360. end;
  361. procedure GetTime(Var Hour, Minute, Second, Sec100: Word);
  362. var
  363. cd : pClockData;
  364. oldtime : ttimeval;
  365. begin
  366. new(cd);
  367. get_sys_time(@oldtime);
  368. Amiga2Date(oldtime.tv_secs,cd);
  369. Hour := cd^.hour;
  370. Minute := cd^.min;
  371. Second := cd^.sec;
  372. Sec100 := oldtime.tv_micro div 10000;
  373. dispose(cd);
  374. end;
  375. Procedure SetTime(Hour, Minute, Second, Sec100: Word);
  376. var
  377. cd : pClockData;
  378. oldtime : ttimeval;
  379. begin
  380. new(cd);
  381. get_sys_time(@oldtime);
  382. Amiga2Date(oldtime.tv_secs,cd);
  383. cd^.hour := Hour;
  384. cd^.min := Minute;
  385. cd^.sec := Second;
  386. set_new_time(Date2Amiga(cd), Sec100 * 10000);
  387. dispose(cd);
  388. end;
  389. function GetMsCount: int64;
  390. var
  391. TV: TTimeVal;
  392. begin
  393. Get_Sys_Time (@TV);
  394. GetMsCount := int64 (TV.TV_Secs) * 1000 + TV.TV_Micro div 1000;
  395. end;
  396. {******************************************************************************
  397. --- Exec ---
  398. ******************************************************************************}
  399. procedure Exec(const Path: PathStr; const ComLine: ComStr);
  400. var
  401. tmpPath: array[0..515] of char;
  402. result : longint;
  403. tmpLock: longint;
  404. begin
  405. DosError:= 0;
  406. LastDosExitCode:=0;
  407. tmpPath:=PathConv(Path)+#0+ComLine+#0; // hacky... :)
  408. { Here we must first check if the command we wish to execute }
  409. { actually exists, because this is NOT handled by the }
  410. { _SystemTagList call (program will abort!!) }
  411. { Try to open with shared lock }
  412. tmpLock:=Lock(tmpPath,SHARED_LOCK);
  413. if tmpLock<>0 then
  414. begin
  415. { File exists - therefore unlock it }
  416. Unlock(tmpLock);
  417. tmpPath[length(Path)]:=' '; // hacky... replaces first #0 from above, to get the whole string. :)
  418. result:=SystemTagList(tmpPath,nil);
  419. { on return of -1 the shell could not be executed }
  420. { probably because there was not enough memory }
  421. if result = -1 then
  422. DosError:=8
  423. else
  424. LastDosExitCode:=word(result);
  425. end
  426. else
  427. DosError:=3;
  428. end;
  429. procedure GetCBreak(Var BreakValue: Boolean);
  430. begin
  431. breakvalue := system.BreakOn;
  432. end;
  433. procedure SetCBreak(BreakValue: Boolean);
  434. begin
  435. system.Breakon := BreakValue;
  436. end;
  437. {******************************************************************************
  438. --- Disk ---
  439. ******************************************************************************}
  440. {
  441. The Diskfree and Disksize functions need a file on the specified drive, since this
  442. is required for the statfs system call.
  443. These filenames are set in drivestr[0..26], and have been preset to :
  444. 0 - ':' (default drive - hence current dir is ok.)
  445. 1 - 'DF0:' (floppy drive 1 - should be adapted to local system )
  446. 2 - 'DF1:' (floppy drive 2 - should be adapted to local system )
  447. 3 - 'SYS:' (C: equivalent of dos is the SYS: partition)
  448. 4..26 (can be set by you're own applications)
  449. ! Use AddDisk() to Add new drives !
  450. They both return -1 when a failure occurs.
  451. }
  452. var
  453. DeviceList: array[0..26] of string[20];
  454. NumDevices: Integer = 0;
  455. const
  456. IllegalDevices: array[0..12] of string =(
  457. 'PED:',
  458. 'PRJ:',
  459. 'PIPE:', // Pipes
  460. 'XPIPE:', // Extented Pipe
  461. 'CON:', // Console
  462. 'RAW:', // RAW: Console
  463. 'KCON:', // KingCON Console
  464. 'KRAW:', // KingCON RAW
  465. 'SER:', // serial Ports
  466. 'SER0:',
  467. 'SER1:',
  468. 'PAR:', // Parallel Porty
  469. 'PRT:'); // Printer
  470. function IsIllegalDevice(DeviceName: string): Boolean;
  471. var
  472. i: Integer;
  473. Str: AnsiString;
  474. begin
  475. IsIllegalDevice := False;
  476. Str := UpCase(DeviceName);
  477. for i := Low(IllegalDevices) to High(IllegalDevices) do
  478. begin
  479. if Str = IllegalDevices[i] then
  480. begin
  481. IsIllegalDevice := True;
  482. Exit;
  483. end;
  484. end;
  485. end;
  486. function DeviceByIdx(Idx: Integer): string;
  487. begin
  488. DeviceByIdx := '';
  489. if (Idx < 0) or (Idx >= NumDevices) then
  490. Exit;
  491. DeviceByIdx := DeviceList[Idx];
  492. end;
  493. function AddDisk(const Path: string): Integer;
  494. begin
  495. // if hit border, restart at 4
  496. if NumDevices > 26 then
  497. NumDevices := 4;
  498. // set the device
  499. DeviceList[NumDevices] := Copy(Path, 1, 20);
  500. // return the Index increment for next run
  501. AddDisk := NumDevices;
  502. Inc(NumDevices);
  503. end;
  504. function RefreshDeviceList: Integer;
  505. var
  506. List: PDosList;
  507. Temp: PChar;
  508. Str: string;
  509. begin
  510. NumDevices := 0;
  511. AddDisk(':'); // Index 0
  512. AddDisk('DF0:'); // Index 1
  513. AddDisk('DF1:'); // Index 2
  514. AddDisk('SYS:'); // Index 3
  515. // Lock the List
  516. List := LockDosList(LDF_DEVICES or LDF_READ);
  517. // Inspect the List
  518. repeat
  519. List := NextDosEntry(List, LDF_DEVICES);
  520. if List <> nil then
  521. begin
  522. Temp := BSTR2STRING(List^.dol_Name);
  523. Str := strpas(Temp) + ':';
  524. if not IsIllegalDevice(str) then
  525. AddDisk(Str);
  526. end;
  527. until List = nil;
  528. UnLockDosList(LDF_DEVICES or LDF_READ);
  529. RefreshDeviceList := NumDevices;
  530. end;
  531. // New easier DiskSize()
  532. //
  533. function DiskSize(Drive: AnsiString): Int64;
  534. var
  535. DirLock: LongInt;
  536. Inf: TInfoData;
  537. MyProc: PProcess;
  538. OldWinPtr: Pointer;
  539. begin
  540. DiskSize := -1;
  541. //
  542. MyProc := PProcess(FindTask(Nil));
  543. OldWinPtr := MyProc^.pr_WindowPtr;
  544. MyProc^.pr_WindowPtr := Pointer(-1);
  545. //
  546. DirLock := Lock(PChar(Drive), SHARED_LOCK);
  547. if DirLock <> 0 then
  548. begin
  549. if Info(DirLock, @Inf) <> 0 then
  550. DiskSize := Int64(Inf.id_NumBlocks) * Inf.id_BytesPerBlock;
  551. UnLock(DirLock);
  552. end;
  553. if OldWinPtr <> Pointer(-1) then
  554. MyProc^.pr_WindowPtr := OldWinPtr;
  555. end;
  556. function DiskSize(Drive: Byte): Int64;
  557. begin
  558. DiskSize := -1;
  559. if (Drive < 0) or (Drive >= NumDevices) then
  560. Exit;
  561. DiskSize := DiskSize(DeviceList[Drive]);
  562. end;
  563. // New easier DiskFree()
  564. //
  565. function DiskFree(Drive: AnsiString): Int64;
  566. var
  567. DirLock: LongInt;
  568. Inf: TInfoData;
  569. MyProc: PProcess;
  570. OldWinPtr: Pointer;
  571. begin
  572. DiskFree := -1;
  573. //
  574. MyProc := PProcess(FindTask(Nil));
  575. OldWinPtr := MyProc^.pr_WindowPtr;
  576. MyProc^.pr_WindowPtr := Pointer(-1);
  577. //
  578. DirLock := Lock(PChar(Drive), SHARED_LOCK);
  579. if DirLock <> 0 then
  580. begin
  581. if Info(DirLock, @Inf) <> 0 then
  582. DiskFree := Int64(Inf.id_NumBlocks - Inf.id_NumBlocksUsed) * Inf.id_BytesPerBlock;
  583. UnLock(DirLock);
  584. end;
  585. if OldWinPtr <> Pointer(-1) then
  586. MyProc^.pr_WindowPtr := OldWinPtr;
  587. end;
  588. function DiskFree(Drive: Byte): Int64;
  589. begin
  590. DiskFree := -1;
  591. if (Drive < 0) or (Drive >= NumDevices) then
  592. Exit;
  593. DiskFree := DiskFree(DeviceList[Drive]);
  594. end;
  595. procedure FindFirst(const Path: PathStr; Attr: Word; Var f: SearchRec);
  596. var
  597. tmpStr: array[0..255] of Char;
  598. Anchor: PAnchorPath;
  599. Result: LongInt;
  600. begin
  601. tmpStr:=PathConv(path)+#0;
  602. DosError:=0;
  603. new(Anchor);
  604. FillChar(Anchor^,sizeof(TAnchorPath),#0);
  605. Result:=MatchFirst(@tmpStr,Anchor);
  606. f.AnchorPtr:=Anchor;
  607. if Result = ERROR_NO_MORE_ENTRIES then
  608. DosError:=18
  609. else
  610. if Result<>0 then DosError:=3;
  611. if DosError=0 then begin
  612. {-------------------------------------------------------------------}
  613. { Here we fill up the SearchRec attribute, but we also do check }
  614. { something else, if the it does not match the mask we are looking }
  615. { for we should go to the next file or directory. }
  616. {-------------------------------------------------------------------}
  617. with Anchor^.ap_Info do begin
  618. f.Time := fib_Date.ds_Days * (24 * 60 * 60) +
  619. fib_Date.ds_Minute * 60 +
  620. fib_Date.ds_Tick div 50;
  621. f.attr := 0;
  622. {*------------------------------------*}
  623. {* Determine if is a file or a folder *}
  624. {*------------------------------------*}
  625. if fib_DirEntryType>0 then f.attr:=f.attr OR DIRECTORY;
  626. {*------------------------------------*}
  627. {* Determine if Read only *}
  628. {* Readonly if R flag on and W flag *}
  629. {* off. *}
  630. {* Should we check also that EXEC *}
  631. {* is zero? for read only? *}
  632. {*------------------------------------*}
  633. if ((fib_Protection and FIBF_READ) <> 0) and
  634. ((fib_Protection and FIBF_WRITE) = 0) then f.attr:=f.attr or READONLY;
  635. f.Name := strpas(fib_FileName);
  636. f.Size := fib_Size;
  637. end; { end with }
  638. end;
  639. end;
  640. procedure FindNext(Var f: SearchRec);
  641. var
  642. Result: longint;
  643. Anchor: PAnchorPath;
  644. begin
  645. DosError:=0;
  646. Result:=MatchNext(f.AnchorPtr);
  647. if Result = ERROR_NO_MORE_ENTRIES then
  648. DosError:=18
  649. else
  650. if Result <> 0 then DosError:=3;
  651. if DosError=0 then begin
  652. { Fill up the Searchrec information }
  653. { and also check if the files are with }
  654. { the correct attributes }
  655. Anchor:=pAnchorPath(f.AnchorPtr);
  656. with Anchor^.ap_Info do begin
  657. f.Time := fib_Date.ds_Days * (24 * 60 * 60) +
  658. fib_Date.ds_Minute * 60 +
  659. fib_Date.ds_Tick div 50;
  660. f.attr := 0;
  661. {*------------------------------------*}
  662. {* Determine if is a file or a folder *}
  663. {*------------------------------------*}
  664. if fib_DirEntryType > 0 then f.attr:=f.attr OR DIRECTORY;
  665. {*------------------------------------*}
  666. {* Determine if Read only *}
  667. {* Readonly if R flag on and W flag *}
  668. {* off. *}
  669. {* Should we check also that EXEC *}
  670. {* is zero? for read only? *}
  671. {*------------------------------------*}
  672. if ((fib_Protection and FIBF_READ) <> 0) and
  673. ((fib_Protection and FIBF_WRITE) = 0) then f.attr:=f.attr or READONLY;
  674. f.Name := strpas(fib_FileName);
  675. f.Size := fib_Size;
  676. end; { end with }
  677. end;
  678. end;
  679. procedure FindClose(Var f: SearchRec);
  680. begin
  681. MatchEnd(f.AnchorPtr);
  682. if assigned(f.AnchorPtr) then
  683. Dispose(PAnchorPath(f.AnchorPtr));
  684. end;
  685. {******************************************************************************
  686. --- File ---
  687. ******************************************************************************}
  688. function FSearch(path: PathStr; dirlist: String) : PathStr;
  689. var
  690. p1 : LongInt;
  691. tmpSR : SearchRec;
  692. newdir : PathStr;
  693. begin
  694. { No wildcards allowed in these things }
  695. if (pos('?',path)<>0) or (pos('*',path)<>0) or (path='') then
  696. FSearch:=''
  697. else begin
  698. repeat
  699. p1:=pos(';',dirlist);
  700. if p1<>0 then begin
  701. newdir:=Copy(dirlist,1,p1-1);
  702. Delete(dirlist,1,p1);
  703. end else begin
  704. newdir:=dirlist;
  705. dirlist:='';
  706. end;
  707. if (newdir<>'') and (not (newdir[length(newdir)] in ['/',':'])) then
  708. newdir:=newdir+'/';
  709. FindFirst(newdir+path,anyfile,tmpSR);
  710. if doserror=0 then
  711. newdir:=newdir+path
  712. else
  713. newdir:='';
  714. until (dirlist='') or (newdir<>'');
  715. FSearch:=newdir;
  716. end;
  717. end;
  718. Procedure getftime (var f; var time : longint);
  719. {
  720. This function returns a file's date and time as the number of
  721. seconds after January 1, 1978 that the file was created.
  722. }
  723. var
  724. FInfo : pFileInfoBlock;
  725. FTime : Longint;
  726. FLock : Longint;
  727. Str : String;
  728. i : integer;
  729. begin
  730. DosError:=0;
  731. FTime := 0;
  732. {$ifdef FPC_ANSI_TEXTFILEREC}
  733. Str := strpas(filerec(f).Name);
  734. {$else}
  735. Str := ToSingleByteFileSystemEncodedFileName(filerec(f).Name);
  736. {$endif}
  737. DoDirSeparators(Str);
  738. FLock := dosLock(Str, SHARED_LOCK);
  739. IF FLock <> 0 then begin
  740. New(FInfo);
  741. if Examine(FLock, FInfo) <> 0 then begin
  742. with FInfo^.fib_Date do
  743. FTime := ds_Days * (24 * 60 * 60) +
  744. ds_Minute * 60 +
  745. ds_Tick div 50;
  746. end else begin
  747. FTime := 0;
  748. end;
  749. Unlock(FLock);
  750. Dispose(FInfo);
  751. end
  752. else
  753. DosError:=6;
  754. time := FTime;
  755. end;
  756. Procedure setftime(var f; time : longint);
  757. var
  758. DateStamp: pDateStamp;
  759. Str: String;
  760. i: Integer;
  761. Days, Minutes,Ticks: longint;
  762. FLock: longint;
  763. Begin
  764. new(DateStamp);
  765. {$ifdef FPC_ANSI_TEXTFILEREC}
  766. Str := strpas(filerec(f).Name);
  767. {$else}
  768. Str := ToSingleByteFileSystemEncodedFileName(filerec(f).Name);
  769. {$endif}
  770. DoDirSeparators(str);
  771. { Check first of all, if file exists }
  772. FLock := dosLock(Str, SHARED_LOCK);
  773. IF FLock <> 0 then
  774. begin
  775. Unlock(FLock);
  776. Amiga2DateStamp(time,Days,Minutes,ticks);
  777. DateStamp^.ds_Days:=Days;
  778. DateStamp^.ds_Minute:=Minutes;
  779. DateStamp^.ds_Tick:=Ticks;
  780. if dosSetFileDate(Str,DateStamp) then
  781. DosError:=0
  782. else
  783. DosError:=6;
  784. end
  785. else
  786. DosError:=2;
  787. if assigned(DateStamp) then Dispose(DateStamp);
  788. End;
  789. procedure getfattr(var f; var attr : word);
  790. var
  791. info : pFileInfoBlock;
  792. MyLock : Longint;
  793. flags: word;
  794. Str: String;
  795. i: integer;
  796. begin
  797. DosError:=0;
  798. flags:=0;
  799. New(info);
  800. {$ifdef FPC_ANSI_TEXTFILEREC}
  801. Str := strpas(filerec(f).Name);
  802. {$else}
  803. Str := ToSingleByteFileSystemEncodedFileName(filerec(f).Name);
  804. {$endif}
  805. DoDirSeparators(str);
  806. { open with shared lock to check if file exists }
  807. MyLock:=dosLock(Str,SHARED_LOCK);
  808. if MyLock <> 0 then
  809. Begin
  810. Examine(MyLock,info);
  811. {*------------------------------------*}
  812. {* Determine if is a file or a folder *}
  813. {*------------------------------------*}
  814. if info^.fib_DirEntryType > 0 then
  815. flags:=flags OR DIRECTORY;
  816. {*------------------------------------*}
  817. {* Determine if Read only *}
  818. {* Readonly if R flag on and W flag *}
  819. {* off. *}
  820. {* Should we check also that EXEC *}
  821. {* is zero? for read only? *}
  822. {*------------------------------------*}
  823. if ((info^.fib_Protection and FIBF_READ) <> 0)
  824. AND ((info^.fib_Protection and FIBF_WRITE) = 0)
  825. then
  826. flags:=flags OR ReadOnly;
  827. Unlock(mylock);
  828. end
  829. else
  830. DosError:=3;
  831. attr:=flags;
  832. Dispose(info);
  833. End;
  834. procedure setfattr(var f; attr : word);
  835. var
  836. flags: longint;
  837. tmpLock : longint;
  838. {$ifndef FPC_ANSI_TEXTFILEREC}
  839. r : rawbytestring;
  840. {$endif not FPC_ANSI_TEXTFILEREC}
  841. p : pchar;
  842. begin
  843. {$ifdef FPC_ANSI_TEXTFILEREC}
  844. p := @filerec(f).Name;
  845. {$else}
  846. r := ToSingleByteFileSystemEncodedFileName(filerec(f).Name);
  847. p := pchar(r);
  848. {$endif}
  849. DosError:=0;
  850. flags:=FIBF_WRITE;
  851. { By default files are read-write }
  852. if attr and ReadOnly <> 0 then flags:=FIBF_READ; { Clear the Fibf_write flags }
  853. { no need for path conversion here, because file opening already }
  854. { converts the path (KB) }
  855. { create a shared lock on the file }
  856. tmpLock:=Lock(p,SHARED_LOCK);
  857. if tmpLock <> 0 then begin
  858. Unlock(tmpLock);
  859. if SetProtection(p,flags) = 0 then DosError:=5;
  860. end else
  861. DosError:=3;
  862. end;
  863. {******************************************************************************
  864. --- Environment ---
  865. ******************************************************************************}
  866. var
  867. strofpaths : string;
  868. function SystemTags(const command: PChar; const tags: array of DWord): LongInt;
  869. begin
  870. SystemTags:=SystemTagList(command,@tags);
  871. end;
  872. function getpathstring: string;
  873. var
  874. f : text;
  875. s : string;
  876. found : boolean;
  877. temp : string[255];
  878. begin
  879. found := true;
  880. temp := '';
  881. { Alternatively, this could use PIPE: handler on systems which
  882. have this by default (not the case on classic Amiga), but then
  883. the child process should be started async, which for a simple
  884. Path command probably isn't worth the trouble. (KB) }
  885. assign(f,'T:'+HexStr(FindTask(nil))+'_path.tmp');
  886. rewrite(f);
  887. { This is a pretty ugly stunt, combining Pascal and Amiga system
  888. functions, but works... }
  889. SystemTags('C:Path',[SYS_Input, 0, SYS_Output, TextRec(f).Handle, TAG_END]);
  890. close(f);
  891. reset(f);
  892. { skip the first line, garbage }
  893. if not eof(f) then readln(f,s);
  894. while not eof(f) do begin
  895. readln(f,s);
  896. if found then begin
  897. temp := s;
  898. found := false;
  899. end else begin
  900. if (length(s) + length(temp)) < 255 then
  901. temp := temp + ';' + s;
  902. end;
  903. end;
  904. close(f);
  905. erase(f);
  906. getpathstring := temp;
  907. end;
  908. var
  909. EnvList: array of record
  910. Name: string;
  911. Local: Boolean;
  912. Value: string;
  913. end;
  914. procedure InitEnvironmentStrings;
  915. Const
  916. BUFFER_SIZE = 254;
  917. Var
  918. ThisProcess: PProcess;
  919. LocalVars_List: PMinList; // Local Var structure in struct process (pr_LocalVarsis is actually a minlist
  920. LocalVar_Node: PLocalVar;
  921. Buffer: array[0..BUFFER_SIZE] of Char; // Buffer to hold a value for GetVar()
  922. TempLen: LongInt; // hold returnlength of GetVar()
  923. // for env: searching
  924. Anchor: TAnchorPath;
  925. Res: Integer;
  926. begin
  927. SetLength(EnvList, 0);
  928. ThisProcess := PProcess(FindTask(nil)); //Get the pointer to our process
  929. LocalVars_List := @(ThisProcess^.pr_LocalVars); //get the list of pr_LocalVars as pointer
  930. LocalVar_Node := pLocalVar(LocalVars_List^.mlh_head); //get the headnode of the LocalVars list
  931. // loop through the localvar list
  932. while ( Pointer(LocalVar_Node^.lv_node.ln_Succ) <> Pointer(LocalVars_List^.mlh_Tail)) do
  933. begin
  934. // make sure the active node is valid instead of empty
  935. If not(LocalVar_Node <> nil) then
  936. break;
  937. { - process the current node - }
  938. If (LocalVar_Node^.lv_node.ln_Type = LV_Var) then
  939. begin
  940. FillChar(Buffer[0], Length(Buffer), #0); // clear Buffer
  941. // get active node's name environment variable value ino buffer and make sure it's local
  942. TempLen := GetVar(LocalVar_Node^.lv_Node.ln_Name, @Buffer[0], BUFFER_SIZE, GVF_LOCAL_ONLY);
  943. If TempLen <> -1 then
  944. begin
  945. SetLength(EnvList, Length(EnvList) + 1);
  946. EnvList[High(EnvList)].Name := LocalVar_Node^.lv_Node.ln_Name;
  947. EnvList[High(EnvList)].Value := string(PChar(@Buffer[0]));
  948. EnvList[High(EnvList)].Local := True;
  949. end;
  950. end;
  951. LocalVar_Node := pLocalVar(LocalVar_Node^.lv_node.ln_Succ); //we need to get the next node
  952. end;
  953. // search in env for all Variables
  954. FillChar(Anchor,sizeof(TAnchorPath),#0);
  955. Res := MatchFirst('ENV:#?', @Anchor);
  956. while Res = 0 do
  957. begin
  958. if Anchor.ap_Info.fib_DirEntryType <= 0 then
  959. begin
  960. SetLength(EnvList, Length(EnvList) + 1);
  961. EnvList[High(EnvList)].Name := Anchor.ap_Info.fib_FileName;
  962. EnvList[High(EnvList)].Value := '';
  963. EnvList[High(EnvList)].Local := False;
  964. end;
  965. Res := MatchNext(@Anchor);
  966. end;
  967. MatchEnd(@Anchor);
  968. // add PATH as Fake Variable:
  969. SetLength(EnvList, Length(EnvList) + 1);
  970. EnvList[High(EnvList)].Name := 'PATH';
  971. EnvList[High(EnvList)].Value := '';
  972. EnvList[High(EnvList)].Local := False;
  973. end;
  974. function EnvCount: Longint;
  975. begin
  976. InitEnvironmentStrings;
  977. EnvCount := Length(EnvList);
  978. end;
  979. function GetEnvFromEnv(envvar : String): String;
  980. var
  981. bufarr : array[0..255] of char;
  982. strbuffer : array[0..255] of char;
  983. temp : Longint;
  984. begin
  985. GetEnvFromEnv := '';
  986. if UpCase(envvar) = 'PATH' then begin
  987. if StrOfpaths = '' then StrOfPaths := GetPathString;
  988. GetEnvFromEnv := StrOfPaths;
  989. end else begin
  990. if (Pos(DriveSeparator,envvar) <> 0) or
  991. (Pos(DirectorySeparator,envvar) <> 0) then exit;
  992. move(envvar[1],strbuffer,length(envvar));
  993. strbuffer[length(envvar)] := #0;
  994. temp := GetVar(strbuffer,bufarr,255,$100);
  995. if temp <> -1 then
  996. GetEnvFromEnv := StrPas(bufarr);
  997. end;
  998. end;
  999. function EnvStr(Index: LongInt): String;
  1000. begin
  1001. EnvStr := '';
  1002. if Length(EnvList) = 0 then
  1003. InitEnvironmentStrings;
  1004. if (Index >= 0) and (Index <= High(EnvList)) then
  1005. begin
  1006. if EnvList[Index].Local then
  1007. EnvStr := EnvList[Index].Name + '=' + EnvList[Index].Value
  1008. else
  1009. EnvStr := EnvList[Index].Name + '=' + GetEnvFromEnv(EnvList[Index].Name);
  1010. end;
  1011. end;
  1012. function GetEnv(envvar : String): String;
  1013. var
  1014. EnvVarName: String;
  1015. i: Integer;
  1016. begin
  1017. GetEnv := '';
  1018. EnvVarName := UpCase(EnvVar);
  1019. if EnvVarName = 'PATH' then
  1020. begin
  1021. if StrOfpaths = '' then
  1022. StrOfPaths := GetPathString;
  1023. GetEnv := StrOfPaths;
  1024. end else
  1025. begin
  1026. InitEnvironmentStrings;
  1027. for i := 0 to High(EnvList) do
  1028. begin
  1029. if EnvVarName = UpCase(EnvList[i].Name) then
  1030. begin
  1031. if EnvList[i].Local then
  1032. GetEnv := EnvList[i].Value
  1033. else
  1034. GetEnv := GetEnvFromEnv(EnvList[i].Name);
  1035. Break;
  1036. end;
  1037. end;
  1038. end;
  1039. end;
  1040. begin
  1041. DosError:=0;
  1042. StrOfPaths := '';
  1043. RefreshDeviceList;
  1044. end.