dos.pp 31 KB

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