dos.pp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2004 by Olle Raab and
  4. members of the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. Unit Dos;
  12. Interface
  13. Uses
  14. macostp;
  15. Const
  16. FileNameLen = 255;
  17. Type
  18. SearchRec = packed record
  19. Attr: Byte; {attribute of found file}
  20. Time: LongInt; {last modify date of found file}
  21. Size: LongInt; {file size of found file}
  22. Reserved: Word; {future use}
  23. Name: string[FileNameLen]; {name of foundfile}
  24. SearchSpec: string[FileNameLen]; {search pattern}
  25. NamePos: Word; {end of path,start of name position}
  26. {MacOS specific params, private, do not use:}
  27. paramBlock: CInfoPBRec;
  28. searchFSSpec: FSSpec;
  29. searchAttr: Byte; {attribute we are searching for}
  30. exactMatch: Boolean;
  31. end;
  32. {$DEFINE HAS_FILENAMELEN}
  33. {$I dosh.inc}
  34. Implementation
  35. {TODO Obtain disk size and disk free values for volumes > 2 GB.
  36. For this, PBXGetVolInfoSync can be used. However, this function
  37. is not available on older versions of Mac OS, so the function has
  38. to be weak linked. An alternative is to directly look into the VCB
  39. (Volume Control Block), but since this is on low leveel it is a
  40. compatibility risque.}
  41. {TODO Perhaps make SearchRec.paramBlock opaque, so that uses macostp;
  42. is not needed in the interface part.}
  43. {TODO Perhaps add some kind of "Procedure AddDisk" for accessing other
  44. volumes. At lest accessing the possible disk drives with
  45. drive number 1 and 2 should be easy.}
  46. {TODO Perhaps use LongDateTime for time functions. But the function
  47. calls must then be weak linked.}
  48. Uses
  49. macutils;
  50. {$UNDEF USE_FEXPAND_INC}
  51. //{$DEFINE USE_FEXPAND_INC}
  52. {$IFNDEF USE_FEXPAND_INC}
  53. {$DEFINE HAS_FEXPAND}
  54. {Own implemetation of fexpand.inc}
  55. {$I dos.inc}
  56. {$ELSE}
  57. {$DEFINE FPC_FEXPAND_VOLUMES}
  58. {$DEFINE FPC_FEXPAND_NO_DEFAULT_PATHS}
  59. {$DEFINE FPC_FEXPAND_DRIVESEP_IS_ROOT}
  60. {$DEFINE FPC_FEXPAND_NO_DOTS_UPDIR}
  61. {$DEFINE FPC_FEXPAND_NO_CURDIR}
  62. { NOTE: If HAS_FEXPAND is not defined, fexpand.inc is included in dos.inc. }
  63. { TODO A lot of issues before this works}
  64. {$I dos.inc}
  65. {$UNDEF FPC_FEXPAND_VOLUMES}
  66. {$UNDEF FPC_FEXPAND_NO_DEFAULT_PATHS}
  67. {$UNDEF FPC_FEXPAND_DRIVESEP_IS_ROOT}
  68. {$UNDEF FPC_FEXPAND_NO_DOTS_UPDIR}
  69. {$UNDEF FPC_FEXPAND_NO_CURDIR}
  70. {$ENDIF}
  71. function MacTimeToDosPackedTime(macfiletime: UInt32): Longint;
  72. var
  73. mdt: DateTimeRec; {Mac OS datastructure}
  74. ddt: Datetime; {Dos OS datastructure}
  75. dospackedtime: Longint;
  76. begin
  77. SecondsToDate(macfiletime, mdt);
  78. with ddt do
  79. begin
  80. year := mdt.year;
  81. month := mdt.month;
  82. day := mdt.day;
  83. hour := mdt.hour;
  84. min := mdt.minute;
  85. sec := mdt.second;
  86. end;
  87. Packtime(ddt, dospackedtime);
  88. MacTimeToDosPackedTime:= dospackedtime;
  89. end;
  90. {******************************************************************************
  91. --- Info / Date / Time ---
  92. ******************************************************************************}
  93. function DosVersion:Word;
  94. begin
  95. DosVersion:=
  96. (macosSystemVersion and $FF00) or
  97. ((macosSystemVersion and $00F0) shr 4);
  98. end;
  99. procedure GetDate (var year, month, mday, wday: word);
  100. var
  101. d: DateTimeRec;
  102. begin
  103. Macostp.GetTime(d);
  104. year := d.year;
  105. month := d.month;
  106. mday := d.day;
  107. wday := d.dayOfWeek - 1; {1-based on mac}
  108. end;
  109. procedure GetTime (var hour, minute, second, sec100: word);
  110. var
  111. d: DateTimeRec;
  112. begin
  113. Macostp.GetTime(d);
  114. hour := d.hour;
  115. minute := d.minute;
  116. second := d.second;
  117. sec100 := 0;
  118. end;
  119. Procedure SetDate(Year, Month, Day: Word);
  120. var
  121. d: DateTimeRec;
  122. Begin
  123. Macostp.GetTime(d);
  124. d.year := year;
  125. d.month := month;
  126. d.day := day;
  127. Macostp.SetTime(d)
  128. End;
  129. Procedure SetTime(Hour, Minute, Second, Sec100: Word);
  130. var
  131. d: DateTimeRec;
  132. Begin
  133. Macostp.GetTime(d);
  134. d.hour := hour;
  135. d.minute := minute;
  136. d.second := second;
  137. Macostp.SetTime(d)
  138. End;
  139. {******************************************************************************
  140. --- Exec ---
  141. ******************************************************************************}
  142. { Create a DoScript AppleEvent that targets the given application with text as the direct object. }
  143. function CreateDoScriptEvent (applCreator: OSType; scriptText: PChar; var theEvent: AppleEvent): OSErr;
  144. var
  145. err: OSErr;
  146. targetAddress: AEDesc;
  147. s: signedByte;
  148. begin
  149. err := AECreateDesc(FourCharCodeToLongword(typeApplSignature), @applCreator, sizeof(applCreator), targetAddress);
  150. if err = noErr then
  151. begin
  152. err := AECreateAppleEvent(FourCharCodeToLongword('misc'), FourCharCodeToLongword('dosc'),
  153. targetAddress, kAutoGenerateReturnID, kAnyTransactionID, theEvent);
  154. if err = noErr then
  155. { Add script text as the direct object parameter. }
  156. err := AEPutParamPtr(theEvent, FourCharCodeToLongword('----'),
  157. FourCharCodeToLongword('TEXT'), scriptText, Length(scriptText));
  158. if err <> noErr then
  159. AEDisposeDesc(theEvent);
  160. AEDisposeDesc(targetAddress);
  161. end;
  162. CreateDoScriptEvent := err;
  163. end;
  164. Procedure Fpc_WriteBuffer(var f:Text;const b;len:longint);[external name 'FPC_WRITEBUFFER'];
  165. {declared in text.inc}
  166. procedure WriteAEDescTypeCharToFile(desc: AEDesc; var f: Text);
  167. begin
  168. if desc.descriptorType = FourCharCodeToLongword(typeChar) then
  169. begin
  170. HLock(desc.dataHandle);
  171. Fpc_WriteBuffer(f, PChar(desc.dataHandle^)^, GetHandleSize(desc.dataHandle));
  172. Flush(f);
  173. HUnLock(desc.dataHandle);
  174. end;
  175. end;
  176. function ExecuteToolserverScript(scriptText: PChar; var statusCode: Longint): OSErr;
  177. var
  178. err: OSErr;
  179. err2: OSErr; {Non serious error}
  180. theEvent: AppleEvent;
  181. reply: AppleEvent;
  182. result: AEDesc;
  183. applFileSpec: FSSpec;
  184. p: SignedByte;
  185. const
  186. applCreator = 'MPSX'; {Toolserver}
  187. begin
  188. statusCode:= 3; //3 according to MPW.
  189. err:= CreateDoScriptEvent (FourCharCodeToLongword(applCreator), scriptText, theEvent);
  190. if err = noErr then
  191. begin
  192. err := AESend(theEvent, reply, kAEWaitReply, kAENormalPriority, kAEDefaultTimeOut, nil, nil);
  193. if err = connectionInvalid then { Toolserver not available }
  194. begin
  195. err := FindApplication(FourCharCodeToLongword(applCreator), applFileSpec);
  196. if err = noErr then
  197. err := LaunchFSSpec(false, applFileSpec);
  198. if err = noErr then
  199. err := AESend(theEvent, reply, kAEWaitReply, kAENormalPriority, kAEDefaultTimeOut, nil, nil);
  200. end;
  201. if err = noErr then
  202. begin
  203. err:= AEGetParamDesc(reply, FourCharCodeToLongword('stat'),
  204. FourCharCodeToLongword(typeLongInteger), result);
  205. if err = noErr then
  206. if result.descriptorType = FourCharCodeToLongword(typeLongInteger) then
  207. statusCode:= LongintPtr(result.dataHandle^)^;
  208. {If there is no output below, we get a non zero error code}
  209. err2:= AEGetParamDesc(reply, FourCharCodeToLongword('----'),
  210. FourCharCodeToLongword(typeChar), result);
  211. if err2 = noErr then
  212. WriteAEDescTypeCharToFile(result, stdout);
  213. err2:= AEGetParamDesc(reply, FourCharCodeToLongword('diag'),
  214. FourCharCodeToLongword(typeChar), result);
  215. if err2 = noErr then
  216. WriteAEDescTypeCharToFile(result, stderr);
  217. AEDisposeDesc(reply);
  218. {$IFDEF TARGET_API_MAC_CARBON }
  219. {$ERROR FIXME AEDesc data is not allowed to be directly accessed}
  220. {$ENDIF}
  221. end;
  222. AEDisposeDesc(theEvent);
  223. end;
  224. ExecuteToolserverScript:= err;
  225. end;
  226. Procedure Exec (Const Path: PathStr; Const ComLine: ComStr);
  227. var
  228. s: AnsiString;
  229. err: OSErr;
  230. wdpath: RawByteString;
  231. Begin
  232. wdpath:='';
  233. {Make ToolServers working directory in sync with our working directory}
  234. PathArgToFullPath(':', wdpath);
  235. wdpath:= 'Directory ''' + wdpath + '''';
  236. err:= ExecuteToolserverScript(PChar(wdpath), LastDosExitCode);
  237. {TODO Only change path when actually needed. But this requires some
  238. change counter to be incremented each time wd is changed. }
  239. s:= path + ' ' + comline;
  240. err:= ExecuteToolserverScript(PChar(s), LastDosExitCode);
  241. if err = afpItemNotFound then
  242. DosError := 900
  243. else
  244. DosError := MacOSErr2RTEerr(err);
  245. //TODO Better dos error codes
  246. End;
  247. {******************************************************************************
  248. --- Disk ---
  249. ******************************************************************************}
  250. {If drive is 0 the free space on the volume of the working directory is returned.
  251. If drive is 1 or 2, the free space on the first or second floppy disk is returned.
  252. If drive is 3 the free space on the boot volume is returned.
  253. If the free space is > 2 GB, then 2 GB is reported.}
  254. Function DiskFree(drive: Byte): Int64;
  255. var
  256. myHPB: HParamBlockRec;
  257. myErr: OSErr;
  258. begin
  259. myHPB.ioNamePtr := NIL;
  260. myHPB.ioVolIndex := 0;
  261. case drive of
  262. 0: myHPB.ioVRefNum := GetWorkingDirectoryVRefNum;
  263. 1: myHPB.ioVRefNum := 1;
  264. 2: myHPB.ioVRefNum := 2;
  265. 3: myHPB.ioVRefNum := macosBootVolumeVRefNum;
  266. else
  267. begin
  268. Diskfree:= -1;
  269. Exit;
  270. end;
  271. end;
  272. myErr := PBHGetVInfoSync(@myHPB);
  273. if myErr = noErr then
  274. Diskfree := myHPB.ioVAlBlkSiz * myHPB.ioVFrBlk
  275. else
  276. Diskfree:= -1;
  277. End;
  278. {If drive is 0 the size of the volume of the working directory is returned.
  279. If drive is 1 or 2, the size of the first or second floppy disk is returned.
  280. If drive is 3 the size of the boot volume is returned.
  281. If the actual size is > 2 GB, then 2 GB is reported.}
  282. Function DiskSize(drive: Byte): Int64;
  283. var
  284. myHPB: HParamBlockRec;
  285. myErr: OSErr;
  286. Begin
  287. myHPB.ioNamePtr := NIL;
  288. myHPB.ioVolIndex := 0;
  289. case drive of
  290. 0: myHPB.ioVRefNum := GetWorkingDirectoryVRefNum;
  291. 1: myHPB.ioVRefNum := 1;
  292. 2: myHPB.ioVRefNum := 2;
  293. 3: myHPB.ioVRefNum := macosBootVolumeVRefNum;
  294. else
  295. begin
  296. DiskSize:= -1;
  297. Exit;
  298. end;
  299. end;
  300. myErr := PBHGetVInfoSync(@myHPB);
  301. if myErr = noErr then
  302. DiskSize := myHPB.ioVAlBlkSiz * myHPB.ioVNmAlBlks
  303. else
  304. DiskSize:=-1;
  305. End;
  306. {******************************************************************************
  307. --- Findfirst FindNext ---
  308. ******************************************************************************}
  309. function FNMatch (const Pattern, Name: string): Boolean;
  310. var
  311. LenPat, LenName: longint;
  312. function DoFNMatch (i, j: longint): Boolean;
  313. var
  314. Found: boolean;
  315. begin
  316. Found := true;
  317. while Found and (i <= LenPat) do
  318. begin
  319. case Pattern[i] of
  320. '?':
  321. Found := (j <= LenName);
  322. '*':
  323. begin
  324. {find the next character in pattern, different of ? and *}
  325. while Found and (i < LenPat) do
  326. begin
  327. i := i + 1;
  328. case Pattern[i] of
  329. '*':
  330. ;
  331. '?':
  332. begin
  333. j := j + 1;
  334. Found := (j <= LenName);
  335. end;
  336. otherwise
  337. Found := false;
  338. end;
  339. end;
  340. {Now, find in name the character which i points to, if the * or ?}
  341. {wasn 't the last character in the pattern, else, use up all the}
  342. {chars in name }
  343. Found := true;
  344. if (i <= LenPat) then
  345. begin
  346. repeat
  347. {find a letter (not only first !) which maches pattern[i]}
  348. while (j <= LenName) and (name[j] <> pattern[i]) do
  349. j := j + 1;
  350. if (j < LenName) then
  351. begin
  352. if DoFnMatch(i + 1, j + 1) then
  353. begin
  354. i := LenPat;
  355. j := LenName;{we can stop}
  356. Found := true;
  357. end
  358. else
  359. j := j + 1;{We didn't find one, need to look further}
  360. end;
  361. until (j >= LenName);
  362. end
  363. else
  364. j := LenName;{we can stop}
  365. end;
  366. otherwise {not a wildcard character in pattern}
  367. Found := (j <= LenName) and (pattern[i] = name[j]);
  368. end;
  369. i := i + 1;
  370. j := j + 1;
  371. end;
  372. DoFnMatch := Found and (j > LenName);
  373. end;
  374. begin {start FNMatch}
  375. LenPat := Length(Pattern);
  376. LenName := Length(Name);
  377. FNMatch := DoFNMatch(1, 1);
  378. end;
  379. function GetFileAttrFromPB (var paramBlock: CInfoPBRec): Word;
  380. var
  381. isLocked, isInvisible, isDirectory, isNameLocked: Boolean;
  382. attr: Word;
  383. {NOTE "nameLocked" was in pre-System 7 called "isSystem".
  384. It is used for files whose name and icon cannot be changed by the user,
  385. that is essentially system files. However in System 9 the folder
  386. "Applications (Mac OS 9)" also has this attribute, and since this is
  387. not a system file in traditional meaning, we will not use this attribute
  388. as the "sysfile" attribute.}
  389. begin
  390. with paramBlock do
  391. begin
  392. attr := 0;
  393. isDirectory := (ioFlAttrib and $10) <> 0;
  394. if isDirectory then
  395. attr := (attr or directory);
  396. isLocked := (ioFlAttrib and $01) <> 0;
  397. if isLocked then
  398. attr := (attr or readonly);
  399. if not isDirectory then
  400. begin
  401. isInvisible := (ioFlFndrInfo.fdFlags and 16384) <> 0;
  402. (* isNameLocked := (ioFlFndrInfo.fdFlags and 4096) <> 0; *)
  403. end
  404. else
  405. begin
  406. isInvisible := (ioDrUsrWds.frFlags and 16384) <> 0;
  407. (* isNameLocked := (ioDrUsrWds.frFlags and 4096) <> 0; *)
  408. end;
  409. if isInvisible then
  410. attr := (attr or hidden);
  411. (*
  412. if isNameLocked then
  413. attr := (attr or sysfile);
  414. *)
  415. GetFileAttrFromPB := attr;
  416. end;
  417. end;
  418. procedure SetPBFromFileAttr (var paramBlock: CInfoPBRec; attr: Word);
  419. begin
  420. with paramBlock do
  421. begin
  422. (*
  423. {Doesn't seem to work, despite the documentation.}
  424. {Can instead be set by FSpSetFLock/FSpRstFLock}
  425. if (attr and readonly) <> 0 then
  426. ioFlAttrib := (ioFlAttrib or $01)
  427. else
  428. ioFlAttrib := (ioFlAttrib and not($01));
  429. *)
  430. if (attr and hidden) <> 0 then
  431. ioFlFndrInfo.fdFlags := (ioFlFndrInfo.fdFlags or 16384)
  432. else
  433. ioFlFndrInfo.fdFlags := (ioFlFndrInfo.fdFlags and not(16384))
  434. end;
  435. end;
  436. function GetFileSizeFromPB (var paramBlock: CInfoPBRec): Longint;
  437. begin
  438. with paramBlock do
  439. if ((ioFlAttrib and $10) <> 0) then {if directory}
  440. GetFileSizeFromPB := 0
  441. else
  442. GetFileSizeFromPB := ioFlLgLen + ioFlRLgLen; {Add length of both forks}
  443. end;
  444. function DoFindOne (var spec: FSSpec; var paramBlock: CInfoPBRec): Integer;
  445. var
  446. err: OSErr;
  447. begin
  448. with paramBlock do
  449. begin
  450. ioVRefNum := spec.vRefNum;
  451. ioDirID := spec.parID;
  452. ioNamePtr := @spec.name;
  453. ioFDirIndex := 0;
  454. err := PBGetCatInfoSync(@paramBlock);
  455. DoFindOne := MacOSErr2RTEerr(err);
  456. end;
  457. end;
  458. {To be used after a call to DoFindOne, with the same spec and paramBlock.}
  459. {Change those parameters in paramBlock, which is to be changed.}
  460. function DoSetOne (var spec: FSSpec; var paramBlock: CInfoPBRec): Integer;
  461. var
  462. err: OSErr;
  463. begin
  464. with paramBlock do
  465. begin
  466. ioVRefNum := spec.vRefNum;
  467. ioDirID := spec.parID;
  468. ioNamePtr := @spec.name;
  469. err := PBSetCatInfoSync(@paramBlock);
  470. DoSetOne := MacOSErr2RTEerr(err);
  471. end;
  472. end;
  473. procedure DoFind (var F: SearchRec; firstTime: Boolean);
  474. var
  475. err: OSErr;
  476. s: Str255;
  477. begin
  478. with F, paramBlock do
  479. begin
  480. ioVRefNum := searchFSSpec.vRefNum;
  481. if firstTime then
  482. ioFDirIndex := 0;
  483. while true do
  484. begin
  485. s := '';
  486. ioDirID := searchFSSpec.parID;
  487. ioFDirIndex := ioFDirIndex + 1;
  488. ioNamePtr := @s;
  489. err := PBGetCatInfoSync(@paramBlock);
  490. if err <> noErr then
  491. begin
  492. if err = fnfErr then
  493. DosError := 18
  494. else
  495. DosError := MacOSErr2RTEerr(err);
  496. break;
  497. end;
  498. attr := GetFileAttrFromPB(f.paramBlock);
  499. if ((Attr and not(searchAttr)) = 0) then
  500. begin
  501. name := s;
  502. UpperString(s, true);
  503. if FNMatch(F.searchFSSpec.name, s) then
  504. begin
  505. size := GetFileSizeFromPB(paramBlock);
  506. time := MacTimeToDosPackedTime(ioFlMdDat);
  507. DosError := 0;
  508. break;
  509. end;
  510. end;
  511. end;
  512. end;
  513. end;
  514. procedure FindFirst (const path: pathstr; Attr: Word; var F: SearchRec);
  515. var
  516. s: Str255;
  517. begin
  518. fillchar(f, sizeof(f), 0);
  519. if path = '' then
  520. begin
  521. DosError := 3;
  522. Exit;
  523. end;
  524. {We always also search for readonly and archive, regardless of Attr.}
  525. F.searchAttr := (Attr or (archive or readonly));
  526. DosError := PathArgToFSSpec(path, F.searchFSSpec);
  527. with F do
  528. if (DosError = 0) or (DosError = 2) then
  529. begin
  530. SearchSpec := path;
  531. NamePos := Length(path) - Length(searchFSSpec.name);
  532. if (Pos('?', searchFSSpec.name) = 0) and (Pos('*', searchFSSpec.name) = 0) then {No wildcards}
  533. begin {If exact match, we don't have to scan the directory}
  534. exactMatch := true;
  535. DosError := DoFindOne(searchFSSpec, paramBlock);
  536. if DosError = 0 then
  537. begin
  538. Attr := GetFileAttrFromPB(paramBlock);
  539. if ((Attr and not(searchAttr)) = 0) then
  540. begin
  541. name := searchFSSpec.name;
  542. size := GetFileSizeFromPB(paramBlock);
  543. time := MacTimeToDosPackedTime(paramBlock.ioFlMdDat);
  544. end
  545. else
  546. DosError := 18;
  547. end
  548. else if DosError = 2 then
  549. DosError := 18;
  550. end
  551. else
  552. begin
  553. exactMatch := false;
  554. s := searchFSSpec.name;
  555. UpperString(s, true);
  556. F.searchFSSpec.name := s;
  557. DoFind(F, true);
  558. end;
  559. end;
  560. end;
  561. procedure FindNext (var f: searchRec);
  562. begin
  563. if F.exactMatch then
  564. DosError := 18
  565. else
  566. DoFind(F, false);
  567. end;
  568. procedure FindClose (var f: searchRec);
  569. {Note: Even if this routine is empty, this doesn't mean it will}
  570. {be empty in the future. Please use it.}
  571. begin
  572. end;
  573. {******************************************************************************
  574. --- File ---
  575. ******************************************************************************}
  576. function FSearch (path: pathstr; dirlist: string): pathstr;
  577. {Searches for a file 'path' in the working directory and then in the list of }
  578. {directories in 'dirlist' . Returns a valid (possibly relative) path or an }
  579. {empty string if not found . Wildcards are NOT allowed }
  580. {The dirlist can be separated with ; or , but not :}
  581. var
  582. NewDir: string[255];
  583. p1: Longint;
  584. spec: FSSpec;
  585. fpcerr: Integer;
  586. begin
  587. FSearch := '';
  588. if (Length(path) = 0) then
  589. Exit;
  590. {Check for Wild Cards}
  591. if (Pos('?', Path) <> 0) or (Pos('*', Path) <> 0) then
  592. Exit;
  593. if pathTranslation then
  594. path := TranslatePathToMac(path, false);
  595. {Search in working directory, or as full path}
  596. fpcerr := PathArgToFSSpec(path, spec);
  597. if (fpcerr = 0) and not IsDirectory(spec) then
  598. begin
  599. FSearch := path;
  600. Exit;
  601. end
  602. else if not IsMacFullPath(path) then {If full path, we do not need to continue.}
  603. begin
  604. {Replace ';' with native mac PathSeparator (',').}
  605. {Note: we cannot support unix style ':', because it is used as dir separator in MacOS}
  606. for p1 := 1 to length(dirlist) do
  607. if dirlist[p1] = ';' then
  608. dirlist[p1] := PathSeparator;
  609. repeat
  610. p1 := Pos(PathSeparator, DirList);
  611. if p1 = 0 then
  612. p1 := 255;
  613. if pathTranslation then
  614. NewDir := TranslatePathToMac(Copy(DirList, 1, P1 - 1), false)
  615. else
  616. NewDir := Copy(DirList, 1, P1 - 1);
  617. NewDir := ConcatMacPath(NewDir, Path);
  618. Delete(DirList, 1, p1);
  619. fpcerr := PathArgToFSSpec(NewDir, spec);
  620. if fpcerr = 0 then
  621. begin
  622. if IsDirectory(spec) then
  623. NewDir := '';
  624. end
  625. else
  626. NewDir := '';
  627. until (DirList = '') or (Length(NewDir) > 0);
  628. FSearch := NewDir;
  629. end;
  630. end;
  631. {$IFNDEF USE_FEXPAND_INC}
  632. { TODO nonexisting dirs in path's doesnt work (nonexisting files do work)
  633. example: Writeln('FExpand on :nisse:kalle : ', FExpand(':nisse:kalle')); }
  634. function FExpand (const path: pathstr): pathstr;
  635. var
  636. fullpath: RawByteString;
  637. begin
  638. fullpath:='';
  639. DosError:= PathArgToFullPath(path, fullpath);
  640. FExpand:= fullpath;
  641. end;
  642. {$ENDIF USE_FEXPAND_INC}
  643. procedure GetFTime (var f ; var time: longint);
  644. var
  645. spec: FSSpec;
  646. paramBlock: CInfoPBRec;
  647. begin
  648. {$ifdef FPC_ANSI_TEXTFILEREC}
  649. DosError := PathArgToFSSpec(filerec(f).name, spec);
  650. {$else}
  651. DosError := PathArgToFSSpec(ToSingleByteFileSystemEncodedFileName(filerec(f).name), spec);
  652. {$endif}
  653. if (DosError = 0) or (DosError = 2) then
  654. begin
  655. DosError := DoFindOne(spec, paramBlock);
  656. if DosError = 0 then
  657. time := MacTimeToDosPackedTime(paramBlock.ioFlMdDat);
  658. end;
  659. end;
  660. procedure SetFTime (var f ; time: longint);
  661. var
  662. spec: FSSpec;
  663. paramBlock: CInfoPBRec;
  664. d: DateTimeRec; {Mac OS datastructure}
  665. t: datetime;
  666. macfiletime: UInt32;
  667. begin
  668. {$ifdef FPC_ANSI_TEXTFILEREC}
  669. DosError := PathArgToFSSpec(filerec(f).name, spec);
  670. {$else}
  671. DosError := PathArgToFSSpec(ToSingleByteFileSystemEncodedFileName(filerec(f).name), spec);
  672. {$endif}
  673. if (DosError = 0) or (DosError = 2) then
  674. begin
  675. DosError := DoFindOne(spec, paramBlock);
  676. if DosError = 0 then
  677. begin
  678. Unpacktime(time, t);
  679. with t do
  680. begin
  681. d.year := year;
  682. d.month := month;
  683. d.day := day;
  684. d.hour := hour;
  685. d.minute := min;
  686. d.second := sec;
  687. end;
  688. DateToSeconds(d, macfiletime);
  689. paramBlock.ioFlMdDat := macfiletime;
  690. DosError := DoSetOne(spec, paramBlock);
  691. end;
  692. end;
  693. end;
  694. procedure GetFAttr (var f ; var attr: word);
  695. var
  696. spec: FSSpec;
  697. paramBlock: CInfoPBRec;
  698. begin
  699. DosError := PathArgToFSSpec(StrPas(filerec(f).name), spec);
  700. if (DosError = 0) or (DosError = 2) then
  701. begin
  702. DosError := DoFindOne(spec, paramBlock);
  703. if DosError = 0 then
  704. attr := GetFileAttrFromPB(paramBlock);
  705. end;
  706. end;
  707. procedure SetFAttr (var f ; attr: word);
  708. var
  709. spec: FSSpec;
  710. paramBlock: CInfoPBRec;
  711. begin
  712. if (attr and VolumeID) <> 0 then
  713. begin
  714. Doserror := 5;
  715. Exit;
  716. end;
  717. DosError := PathArgToFSSpec(StrPas(filerec(f).name), spec);
  718. if (DosError = 0) or (DosError = 2) then
  719. begin
  720. DosError := DoFindOne(spec, paramBlock);
  721. if DosError = 0 then
  722. begin
  723. SetPBFromFileAttr(paramBlock, attr);
  724. DosError := DoSetOne(spec, paramBlock);
  725. if (paramBlock.ioFlAttrib and $10) = 0 then {check not directory}
  726. if DosError = 0 then
  727. if (attr and readonly) <> 0 then
  728. DosError := MacOSErr2RTEerr(FSpSetFLock(spec))
  729. else
  730. DosError := MacOSErr2RTEerr(FSpRstFLock(spec));
  731. end;
  732. end;
  733. end;
  734. {******************************************************************************
  735. --- Environment ---
  736. ******************************************************************************}
  737. Function EnvCount: Longint;
  738. var
  739. envcnt : longint;
  740. p : ppchar;
  741. Begin
  742. envcnt:=0;
  743. p:=envp; {defined in system}
  744. while (p^<>nil) do
  745. begin
  746. inc(envcnt);
  747. inc(p);
  748. end;
  749. EnvCount := envcnt
  750. End;
  751. Function EnvStr (Index: longint): String;
  752. Var
  753. i : longint;
  754. p : ppchar;
  755. Begin
  756. if Index <= 0 then
  757. envstr:=''
  758. else
  759. begin
  760. p:=envp; {defined in system}
  761. i:=1;
  762. while (i<Index) and (p^<>nil) do
  763. begin
  764. inc(i);
  765. inc(p);
  766. end;
  767. if p=nil then
  768. envstr:=''
  769. else
  770. envstr:=strpas(p^) + '=' + strpas(p^+strlen(p^)+1);
  771. end;
  772. end;
  773. function c_getenv(varname: PChar): PChar; {TODO perhaps move to a separate inc file.}
  774. external 'StdCLib' name 'getenv';
  775. Function GetEnv(EnvVar: String): String;
  776. var
  777. p: PChar;
  778. name: String;
  779. Begin
  780. name:= EnvVar+#0;
  781. p:= c_getenv(@name[1]);
  782. if p=nil then
  783. GetEnv:=''
  784. else
  785. GetEnv:=StrPas(p);
  786. End;
  787. {
  788. Procedure GetCBreak(Var BreakValue: Boolean);
  789. Begin
  790. -- Might be implemented in future on MacOS to handle Cmd-. (period) key press
  791. End;
  792. Procedure SetCBreak(BreakValue: Boolean);
  793. Begin
  794. -- Might be implemented in future on MacOS to handle Cmd-. (period) key press
  795. End;
  796. Procedure GetVerify(Var Verify: Boolean);
  797. Begin
  798. -- Might be implemented in future on MacOS
  799. End;
  800. Procedure SetVerify(Verify: Boolean);
  801. Begin
  802. -- Might be implemented in future on MacOS
  803. End;
  804. }
  805. {******************************************************************************
  806. --- Initialization ---
  807. ******************************************************************************}
  808. End.