dos.pp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173
  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. RefreshDeviceList := NumDevices;
  529. end;
  530. // New easier DiskSize()
  531. //
  532. function DiskSize(Drive: AnsiString): Int64;
  533. var
  534. DirLock: LongInt;
  535. Inf: TInfoData;
  536. MyProc: PProcess;
  537. OldWinPtr: Pointer;
  538. begin
  539. DiskSize := -1;
  540. //
  541. MyProc := PProcess(FindTask(Nil));
  542. OldWinPtr := MyProc^.pr_WindowPtr;
  543. MyProc^.pr_WindowPtr := Pointer(-1);
  544. //
  545. DirLock := Lock(PChar(Drive), SHARED_LOCK);
  546. if DirLock <> 0 then
  547. begin
  548. if Info(DirLock, @Inf) <> 0 then
  549. DiskSize := Int64(Inf.id_NumBlocks) * Inf.id_BytesPerBlock;
  550. UnLock(DirLock);
  551. end;
  552. if OldWinPtr <> Pointer(-1) then
  553. MyProc^.pr_WindowPtr := OldWinPtr;
  554. end;
  555. function DiskSize(Drive: Byte): Int64;
  556. begin
  557. DiskSize := -1;
  558. if (Drive < 0) or (Drive >= NumDevices) then
  559. Exit;
  560. DiskSize := DiskSize(DeviceList[Drive]);
  561. end;
  562. // New easier DiskFree()
  563. //
  564. function DiskFree(Drive: AnsiString): Int64;
  565. var
  566. DirLock: LongInt;
  567. Inf: TInfoData;
  568. MyProc: PProcess;
  569. OldWinPtr: Pointer;
  570. begin
  571. DiskFree := -1;
  572. //
  573. MyProc := PProcess(FindTask(Nil));
  574. OldWinPtr := MyProc^.pr_WindowPtr;
  575. MyProc^.pr_WindowPtr := Pointer(-1);
  576. //
  577. DirLock := Lock(PChar(Drive), SHARED_LOCK);
  578. if DirLock <> 0 then
  579. begin
  580. if Info(DirLock, @Inf) <> 0 then
  581. DiskFree := Int64(Inf.id_NumBlocks - Inf.id_NumBlocksUsed) * Inf.id_BytesPerBlock;
  582. UnLock(DirLock);
  583. end;
  584. if OldWinPtr <> Pointer(-1) then
  585. MyProc^.pr_WindowPtr := OldWinPtr;
  586. end;
  587. function DiskFree(Drive: Byte): Int64;
  588. begin
  589. DiskFree := -1;
  590. if (Drive < 0) or (Drive >= NumDevices) then
  591. Exit;
  592. DiskFree := DiskSize(DeviceList[Drive]);
  593. end;
  594. procedure FindFirst(const Path: PathStr; Attr: Word; Var f: SearchRec);
  595. var
  596. tmpStr: array[0..255] of Char;
  597. Anchor: PAnchorPath;
  598. Result: LongInt;
  599. begin
  600. tmpStr:=PathConv(path)+#0;
  601. DosError:=0;
  602. new(Anchor);
  603. FillChar(Anchor^,sizeof(TAnchorPath),#0);
  604. Result:=MatchFirst(@tmpStr,Anchor);
  605. f.AnchorPtr:=Anchor;
  606. if Result = ERROR_NO_MORE_ENTRIES then
  607. DosError:=18
  608. else
  609. if Result<>0 then DosError:=3;
  610. if DosError=0 then begin
  611. {-------------------------------------------------------------------}
  612. { Here we fill up the SearchRec attribute, but we also do check }
  613. { something else, if the it does not match the mask we are looking }
  614. { for we should go to the next file or directory. }
  615. {-------------------------------------------------------------------}
  616. with Anchor^.ap_Info do begin
  617. f.Time := fib_Date.ds_Days * (24 * 60 * 60) +
  618. fib_Date.ds_Minute * 60 +
  619. fib_Date.ds_Tick div 50;
  620. f.attr := 0;
  621. {*------------------------------------*}
  622. {* Determine if is a file or a folder *}
  623. {*------------------------------------*}
  624. if fib_DirEntryType>0 then f.attr:=f.attr OR DIRECTORY;
  625. {*------------------------------------*}
  626. {* Determine if Read only *}
  627. {* Readonly if R flag on and W flag *}
  628. {* off. *}
  629. {* Should we check also that EXEC *}
  630. {* is zero? for read only? *}
  631. {*------------------------------------*}
  632. if ((fib_Protection and FIBF_READ) <> 0) and
  633. ((fib_Protection and FIBF_WRITE) = 0) then f.attr:=f.attr or READONLY;
  634. f.Name := strpas(fib_FileName);
  635. f.Size := fib_Size;
  636. end; { end with }
  637. end;
  638. end;
  639. procedure FindNext(Var f: SearchRec);
  640. var
  641. Result: longint;
  642. Anchor: PAnchorPath;
  643. begin
  644. DosError:=0;
  645. Result:=MatchNext(f.AnchorPtr);
  646. if Result = ERROR_NO_MORE_ENTRIES then
  647. DosError:=18
  648. else
  649. if Result <> 0 then DosError:=3;
  650. if DosError=0 then begin
  651. { Fill up the Searchrec information }
  652. { and also check if the files are with }
  653. { the correct attributes }
  654. Anchor:=pAnchorPath(f.AnchorPtr);
  655. with Anchor^.ap_Info do begin
  656. f.Time := fib_Date.ds_Days * (24 * 60 * 60) +
  657. fib_Date.ds_Minute * 60 +
  658. fib_Date.ds_Tick div 50;
  659. f.attr := 0;
  660. {*------------------------------------*}
  661. {* Determine if is a file or a folder *}
  662. {*------------------------------------*}
  663. if fib_DirEntryType > 0 then f.attr:=f.attr OR DIRECTORY;
  664. {*------------------------------------*}
  665. {* Determine if Read only *}
  666. {* Readonly if R flag on and W flag *}
  667. {* off. *}
  668. {* Should we check also that EXEC *}
  669. {* is zero? for read only? *}
  670. {*------------------------------------*}
  671. if ((fib_Protection and FIBF_READ) <> 0) and
  672. ((fib_Protection and FIBF_WRITE) = 0) then f.attr:=f.attr or READONLY;
  673. f.Name := strpas(fib_FileName);
  674. f.Size := fib_Size;
  675. end; { end with }
  676. end;
  677. end;
  678. procedure FindClose(Var f: SearchRec);
  679. begin
  680. MatchEnd(f.AnchorPtr);
  681. if assigned(f.AnchorPtr) then
  682. Dispose(PAnchorPath(f.AnchorPtr));
  683. end;
  684. {******************************************************************************
  685. --- File ---
  686. ******************************************************************************}
  687. function FSearch(path: PathStr; dirlist: String) : PathStr;
  688. var
  689. p1 : LongInt;
  690. tmpSR : SearchRec;
  691. newdir : PathStr;
  692. begin
  693. { No wildcards allowed in these things }
  694. if (pos('?',path)<>0) or (pos('*',path)<>0) or (path='') then
  695. FSearch:=''
  696. else begin
  697. repeat
  698. p1:=pos(';',dirlist);
  699. if p1<>0 then begin
  700. newdir:=Copy(dirlist,1,p1-1);
  701. Delete(dirlist,1,p1);
  702. end else begin
  703. newdir:=dirlist;
  704. dirlist:='';
  705. end;
  706. if (newdir<>'') and (not (newdir[length(newdir)] in ['/',':'])) then
  707. newdir:=newdir+'/';
  708. FindFirst(newdir+path,anyfile,tmpSR);
  709. if doserror=0 then
  710. newdir:=newdir+path
  711. else
  712. newdir:='';
  713. until (dirlist='') or (newdir<>'');
  714. FSearch:=newdir;
  715. end;
  716. end;
  717. Procedure getftime (var f; var time : longint);
  718. {
  719. This function returns a file's date and time as the number of
  720. seconds after January 1, 1978 that the file was created.
  721. }
  722. var
  723. FInfo : pFileInfoBlock;
  724. FTime : Longint;
  725. FLock : Longint;
  726. Str : String;
  727. i : integer;
  728. begin
  729. DosError:=0;
  730. FTime := 0;
  731. {$ifdef FPC_ANSI_TEXTFILEREC}
  732. Str := strpas(filerec(f).Name);
  733. {$else}
  734. Str := ToSingleByteFileSystemEncodedFileName(filerec(f).Name);
  735. {$endif}
  736. DoDirSeparators(Str);
  737. FLock := dosLock(Str, SHARED_LOCK);
  738. IF FLock <> 0 then begin
  739. New(FInfo);
  740. if Examine(FLock, FInfo) <> 0 then begin
  741. with FInfo^.fib_Date do
  742. FTime := ds_Days * (24 * 60 * 60) +
  743. ds_Minute * 60 +
  744. ds_Tick div 50;
  745. end else begin
  746. FTime := 0;
  747. end;
  748. Unlock(FLock);
  749. Dispose(FInfo);
  750. end
  751. else
  752. DosError:=6;
  753. time := FTime;
  754. end;
  755. Procedure setftime(var f; time : longint);
  756. var
  757. DateStamp: pDateStamp;
  758. Str: String;
  759. i: Integer;
  760. Days, Minutes,Ticks: longint;
  761. FLock: longint;
  762. Begin
  763. new(DateStamp);
  764. {$ifdef FPC_ANSI_TEXTFILEREC}
  765. Str := strpas(filerec(f).Name);
  766. {$else}
  767. Str := ToSingleByteFileSystemEncodedFileName(filerec(f).Name);
  768. {$endif}
  769. DoDirSeparators(str);
  770. { Check first of all, if file exists }
  771. FLock := dosLock(Str, SHARED_LOCK);
  772. IF FLock <> 0 then
  773. begin
  774. Unlock(FLock);
  775. Amiga2DateStamp(time,Days,Minutes,ticks);
  776. DateStamp^.ds_Days:=Days;
  777. DateStamp^.ds_Minute:=Minutes;
  778. DateStamp^.ds_Tick:=Ticks;
  779. if dosSetFileDate(Str,DateStamp) then
  780. DosError:=0
  781. else
  782. DosError:=6;
  783. end
  784. else
  785. DosError:=2;
  786. if assigned(DateStamp) then Dispose(DateStamp);
  787. End;
  788. procedure getfattr(var f; var attr : word);
  789. var
  790. info : pFileInfoBlock;
  791. MyLock : Longint;
  792. flags: word;
  793. Str: String;
  794. i: integer;
  795. begin
  796. DosError:=0;
  797. flags:=0;
  798. New(info);
  799. {$ifdef FPC_ANSI_TEXTFILEREC}
  800. Str := strpas(filerec(f).Name);
  801. {$else}
  802. Str := ToSingleByteFileSystemEncodedFileName(filerec(f).Name);
  803. {$endif}
  804. DoDirSeparators(str);
  805. { open with shared lock to check if file exists }
  806. MyLock:=dosLock(Str,SHARED_LOCK);
  807. if MyLock <> 0 then
  808. Begin
  809. Examine(MyLock,info);
  810. {*------------------------------------*}
  811. {* Determine if is a file or a folder *}
  812. {*------------------------------------*}
  813. if info^.fib_DirEntryType > 0 then
  814. flags:=flags OR DIRECTORY;
  815. {*------------------------------------*}
  816. {* Determine if Read only *}
  817. {* Readonly if R flag on and W flag *}
  818. {* off. *}
  819. {* Should we check also that EXEC *}
  820. {* is zero? for read only? *}
  821. {*------------------------------------*}
  822. if ((info^.fib_Protection and FIBF_READ) <> 0)
  823. AND ((info^.fib_Protection and FIBF_WRITE) = 0)
  824. then
  825. flags:=flags OR ReadOnly;
  826. Unlock(mylock);
  827. end
  828. else
  829. DosError:=3;
  830. attr:=flags;
  831. Dispose(info);
  832. End;
  833. procedure setfattr(var f; attr : word);
  834. var
  835. flags: longint;
  836. tmpLock : longint;
  837. {$ifndef FPC_ANSI_TEXTFILEREC}
  838. r : rawbytestring;
  839. {$endif not FPC_ANSI_TEXTFILEREC}
  840. p : pchar;
  841. begin
  842. {$ifdef FPC_ANSI_TEXTFILEREC}
  843. p := @filerec(f).Name;
  844. {$else}
  845. r := ToSingleByteFileSystemEncodedFileName(filerec(f).Name);
  846. p := pchar(r);
  847. {$endif}
  848. DosError:=0;
  849. flags:=FIBF_WRITE;
  850. { By default files are read-write }
  851. if attr and ReadOnly <> 0 then flags:=FIBF_READ; { Clear the Fibf_write flags }
  852. { no need for path conversion here, because file opening already }
  853. { converts the path (KB) }
  854. { create a shared lock on the file }
  855. tmpLock:=Lock(p,SHARED_LOCK);
  856. if tmpLock <> 0 then begin
  857. Unlock(tmpLock);
  858. if SetProtection(p,flags) = 0 then DosError:=5;
  859. end else
  860. DosError:=3;
  861. end;
  862. {******************************************************************************
  863. --- Environment ---
  864. ******************************************************************************}
  865. var
  866. strofpaths : string;
  867. function SystemTags(const command: PChar; const tags: array of DWord): LongInt;
  868. begin
  869. SystemTags:=SystemTagList(command,@tags);
  870. end;
  871. function getpathstring: string;
  872. var
  873. f : text;
  874. s : string;
  875. found : boolean;
  876. temp : string[255];
  877. begin
  878. found := true;
  879. temp := '';
  880. { Alternatively, this could use PIPE: handler on systems which
  881. have this by default (not the case on classic Amiga), but then
  882. the child process should be started async, which for a simple
  883. Path command probably isn't worth the trouble. (KB) }
  884. assign(f,'T:'+HexStr(FindTask(nil))+'_path.tmp');
  885. rewrite(f);
  886. { This is a pretty ugly stunt, combining Pascal and Amiga system
  887. functions, but works... }
  888. SystemTags('C:Path',[SYS_Input, 0, SYS_Output, TextRec(f).Handle, TAG_END]);
  889. close(f);
  890. reset(f);
  891. { skip the first line, garbage }
  892. if not eof(f) then readln(f,s);
  893. while not eof(f) do begin
  894. readln(f,s);
  895. if found then begin
  896. temp := s;
  897. found := false;
  898. end else begin
  899. if (length(s) + length(temp)) < 255 then
  900. temp := temp + ';' + s;
  901. end;
  902. end;
  903. close(f);
  904. erase(f);
  905. getpathstring := temp;
  906. end;
  907. var
  908. EnvList: array of record
  909. Name: string;
  910. Local: Boolean;
  911. Value: string;
  912. end;
  913. procedure InitEnvironmentStrings;
  914. Const
  915. BUFFER_SIZE = 254;
  916. Var
  917. ThisProcess: PProcess;
  918. LocalVars_List: PMinList; // Local Var structure in struct process (pr_LocalVarsis is actually a minlist
  919. LocalVar_Node: PLocalVar;
  920. Buffer: array[0..BUFFER_SIZE] of Char; // Buffer to hold a value for GetVar()
  921. TempLen: LongInt; // hold returnlength of GetVar()
  922. // for env: searching
  923. Anchor: TAnchorPath;
  924. Res: Integer;
  925. begin
  926. SetLength(EnvList, 0);
  927. ThisProcess := PProcess(FindTask(nil)); //Get the pointer to our process
  928. LocalVars_List := @(ThisProcess^.pr_LocalVars); //get the list of pr_LocalVars as pointer
  929. LocalVar_Node := pLocalVar(LocalVars_List^.mlh_head); //get the headnode of the LocalVars list
  930. // loop through the localvar list
  931. while ( Pointer(LocalVar_Node^.lv_node.ln_Succ) <> Pointer(LocalVars_List^.mlh_Tail)) do
  932. begin
  933. // make sure the active node is valid instead of empty
  934. If not(LocalVar_Node <> nil) then
  935. break;
  936. { - process the current node - }
  937. If (LocalVar_Node^.lv_node.ln_Type = LV_Var) then
  938. begin
  939. FillChar(Buffer[0], Length(Buffer), #0); // clear Buffer
  940. // get active node's name environment variable value ino buffer and make sure it's local
  941. TempLen := GetVar(LocalVar_Node^.lv_Node.ln_Name, @Buffer[0], BUFFER_SIZE, GVF_LOCAL_ONLY);
  942. If TempLen <> -1 then
  943. begin
  944. SetLength(EnvList, Length(EnvList) + 1);
  945. EnvList[High(EnvList)].Name := LocalVar_Node^.lv_Node.ln_Name;
  946. EnvList[High(EnvList)].Value := string(PChar(@Buffer[0]));
  947. EnvList[High(EnvList)].Local := True;
  948. end;
  949. end;
  950. LocalVar_Node := pLocalVar(LocalVar_Node^.lv_node.ln_Succ); //we need to get the next node
  951. end;
  952. // search in env for all Variables
  953. FillChar(Anchor,sizeof(TAnchorPath),#0);
  954. Res := MatchFirst('ENV:#?', @Anchor);
  955. while Res = 0 do
  956. begin
  957. if Anchor.ap_Info.fib_DirEntryType <= 0 then
  958. begin
  959. SetLength(EnvList, Length(EnvList) + 1);
  960. EnvList[High(EnvList)].Name := Anchor.ap_Info.fib_FileName;
  961. EnvList[High(EnvList)].Value := '';
  962. EnvList[High(EnvList)].Local := False;
  963. end;
  964. Res := MatchNext(@Anchor);
  965. end;
  966. MatchEnd(@Anchor);
  967. // add PATH as Fake Variable:
  968. SetLength(EnvList, Length(EnvList) + 1);
  969. EnvList[High(EnvList)].Name := 'PATH';
  970. EnvList[High(EnvList)].Value := '';
  971. EnvList[High(EnvList)].Local := False;
  972. end;
  973. function EnvCount: Longint;
  974. begin
  975. InitEnvironmentStrings;
  976. EnvCount := Length(EnvList);
  977. end;
  978. function GetEnvFromEnv(envvar : String): String;
  979. var
  980. bufarr : array[0..255] of char;
  981. strbuffer : array[0..255] of char;
  982. temp : Longint;
  983. begin
  984. GetEnvFromEnv := '';
  985. if UpCase(envvar) = 'PATH' then begin
  986. if StrOfpaths = '' then StrOfPaths := GetPathString;
  987. GetEnvFromEnv := StrOfPaths;
  988. end else begin
  989. if (Pos(DriveSeparator,envvar) <> 0) or
  990. (Pos(DirectorySeparator,envvar) <> 0) then exit;
  991. move(envvar[1],strbuffer,length(envvar));
  992. strbuffer[length(envvar)] := #0;
  993. temp := GetVar(strbuffer,bufarr,255,$100);
  994. if temp <> -1 then
  995. GetEnvFromEnv := StrPas(bufarr);
  996. end;
  997. end;
  998. function EnvStr(Index: LongInt): String;
  999. begin
  1000. EnvStr := '';
  1001. if Length(EnvList) = 0 then
  1002. InitEnvironmentStrings;
  1003. if (Index >= 0) and (Index <= High(EnvList)) then
  1004. begin
  1005. if EnvList[Index].Local then
  1006. EnvStr := EnvList[Index].Name + '=' + EnvList[Index].Value
  1007. else
  1008. EnvStr := EnvList[Index].Name + '=' + GetEnvFromEnv(EnvList[Index].Name);
  1009. end;
  1010. end;
  1011. function GetEnv(envvar : String): String;
  1012. var
  1013. EnvVarName: String;
  1014. i: Integer;
  1015. begin
  1016. GetEnv := '';
  1017. EnvVarName := UpCase(EnvVar);
  1018. if EnvVarName = 'PATH' then
  1019. begin
  1020. if StrOfpaths = '' then
  1021. StrOfPaths := GetPathString;
  1022. GetEnv := StrOfPaths;
  1023. end else
  1024. begin
  1025. InitEnvironmentStrings;
  1026. for i := 0 to High(EnvList) do
  1027. begin
  1028. if EnvVarName = UpCase(EnvList[i].Name) then
  1029. begin
  1030. if EnvList[i].Local then
  1031. GetEnv := EnvList[i].Value
  1032. else
  1033. GetEnv := GetEnvFromEnv(EnvList[i].Name);
  1034. Break;
  1035. end;
  1036. end;
  1037. end;
  1038. end;
  1039. begin
  1040. DosError:=0;
  1041. StrOfPaths := '';
  1042. RefreshDeviceList;
  1043. end.