dos.pp 31 KB

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