dos.pp 31 KB

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