macutils.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 2004 by Olle Raab
  5. Some utilities specific for Mac OS
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {NOTE: This file requires the following global variables to be declared:
  13. workingDirectorySpec: FSSpec;}
  14. function FourCharCodeToLongword(fourcharcode: Shortstring): Longword;
  15. begin
  16. FourCharCodeToLongword:=
  17. (ord(fourcharcode[1]) shl 24) or
  18. (ord(fourcharcode[2]) shl 16) or
  19. (ord(fourcharcode[3]) shl 8) or
  20. (ord(fourcharcode[4]))
  21. end;
  22. function BitIsSet(arg: Longint; bitnr: Integer): Boolean;
  23. begin
  24. BitIsSet:= (arg and (1 shl bitnr)) <> 0;
  25. end;
  26. { Converts MacOS specific error codes to the correct FPC error code.
  27. All non zero MacOS errors corresponds to a nonzero FPC error.}
  28. Function MacOSErr2RTEerr(err: OSErr): Integer;
  29. var
  30. res: Integer;
  31. begin
  32. if err = noErr then { Else it will go through all the cases }
  33. res:= 0
  34. else case err of
  35. dirFulErr, { Directory full }
  36. dskFulErr { disk full }
  37. :res:=101;
  38. nsvErr { no such volume }
  39. :res:=3;
  40. ioErr, { I/O error (bummers) }
  41. bdNamErr { there may be no bad names in the final system! }
  42. :res:=1; //TODO Exchange to something better
  43. fnOpnErr { File not open }
  44. :res:=103;
  45. eofErr, { End of file }
  46. posErr { tried to position to before start of file (r/w) }
  47. :res:=100;
  48. mFulErr { memory full (open) or file won't fit (load) }
  49. :res:=1; //TODO Exchange to something better
  50. tmfoErr { too many files open}
  51. :res:=4;
  52. fnfErr { File not found }
  53. :res:=2;
  54. wPrErr { diskette is write protected. }
  55. :res:=150;
  56. fLckdErr { file is locked }
  57. :res:=5;
  58. vLckdErr { volume is locked }
  59. :res:=150;
  60. fBsyErr { File is busy (delete) }
  61. :res:=5;
  62. dupFNErr { duplicate filename (rename) }
  63. :res:=5;
  64. opWrErr { file already open with with write permission }
  65. :res:=5;
  66. rfNumErr, { refnum error }
  67. gfpErr { get file position error }
  68. :res:=1; //TODO Exchange to something better
  69. volOffLinErr { volume not on line error (was Ejected) }
  70. :res:=152;
  71. permErr { permissions error (on file open) }
  72. :res:=5;
  73. volOnLinErr{ drive volume already on-line at MountVol }
  74. :res:=1; //TODO Exchange to something other
  75. nsDrvErr { no such drive (tried to mount a bad drive num) }
  76. :res:=1; //TODO Perhaps exchange to something better
  77. noMacDskErr, { not a mac diskette (sig bytes are wrong) }
  78. extFSErr { volume in question belongs to an external fs }
  79. :res:=157; //TODO Perhaps exchange to something better
  80. fsRnErr, { file system internal error:during rename the old
  81. entry was deleted but could not be restored. }
  82. badMDBErr { bad master directory block }
  83. :res:=1; //TODO Exchange to something better
  84. wrPermErr { write permissions error }
  85. :res:=5;
  86. dirNFErr { Directory not found }
  87. :res:=3;
  88. tmwdoErr { No free WDCB available }
  89. :res:=1; //TODO Exchange to something better
  90. badMovErr { Move into offspring error }
  91. :res:=5;
  92. wrgVolTypErr { Wrong volume type error [operation not
  93. supported for MFS] }
  94. :res:=1; //TODO Exchange to something better
  95. volGoneErr { Server volume has been disconnected. }
  96. :res:=152;
  97. diffVolErr { files on different volumes }
  98. :res:=17;
  99. catChangedErr { the catalog has been modified }
  100. { OR comment: when searching with PBCatSearch }
  101. :res:=1; //TODO Exchange to something other
  102. afpAccessDenied, { Insufficient access privileges for operation }
  103. afpDenyConflict { Specified open/deny modes conflict with current open modes }
  104. :res:=5;
  105. afpNoMoreLocks { Maximum lock limit reached }
  106. :res:=5;
  107. afpRangeNotLocked, { Tried to unlock range that was not locked by user }
  108. afpRangeOverlap { Some or all of range already locked by same user }
  109. :res:=1; //TODO Exchange to something better
  110. afpObjectTypeErr { File/Directory specified where Directory/File expected }
  111. :res:=3;
  112. afpCatalogChanged { OR comment: when searching with PBCatSearch }
  113. :res:=1; //TODO Exchange to something other
  114. afpSameObjectErr
  115. :res:=5; //TODO Exchange to something better
  116. memFullErr { Not enough room in heap zone }
  117. :res:=203;
  118. else
  119. res := 1; //TODO Exchange to something better
  120. end;
  121. MacOSErr2RTEerr:= res;
  122. end;
  123. {Translates a unix or dos path to a mac path. Even a mac path can be input, }
  124. {then it is returned as is. A trailing directory separator in input}
  125. {will result in a trailing mac directory separator. For absolute paths, the }
  126. {parameter mpw affects how the root volume is denoted. If mpw is true, }
  127. {the path is intended for use in MPW, and the environment variable Boot is}
  128. {prepended. Otherwise the actual boot volume name is appended.}
  129. {All kinds of paths are attempted to be translated, except the unusal }
  130. {dos construct: a relative path on a certain drive like : C:xxx\yyy}
  131. function TranslatePathToMac (const path: string; mpw: Boolean): string;
  132. function GetVolumeIdentifier: string;
  133. var
  134. s: Str255;
  135. dummy: Integer;
  136. err: OSErr;
  137. begin
  138. if mpw then
  139. GetVolumeIdentifier := '{Boot}'
  140. else
  141. GetVolumeIdentifier := macosBootVolumeName;
  142. end;
  143. var
  144. slashPos, oldpos, newpos, oldlen, maxpos: Longint;
  145. begin
  146. oldpos := 1;
  147. slashPos := Pos('/', path);
  148. if (slashPos <> 0) then {its a unix path}
  149. begin
  150. if slashPos = 1 then
  151. begin {its a full path}
  152. oldpos := 2;
  153. TranslatePathToMac := GetVolumeIdentifier;
  154. end
  155. else {its a partial path}
  156. TranslatePathToMac := ':';
  157. end
  158. else
  159. begin
  160. slashPos := Pos('\', path);
  161. if (slashPos <> 0) then {its a dos path}
  162. begin
  163. if slashPos = 1 then
  164. begin {its a full path, without drive letter}
  165. oldpos := 2;
  166. TranslatePathToMac := GetVolumeIdentifier;
  167. end
  168. else if (Length(path) >= 2) and (path[2] = ':') then {its a full path, with drive letter}
  169. begin
  170. oldpos := 4;
  171. TranslatePathToMac := GetVolumeIdentifier;
  172. end
  173. else {its a partial path}
  174. TranslatePathToMac := ':';
  175. end;
  176. end;
  177. if (slashPos <> 0) then {its a unix or dos path}
  178. begin
  179. {Translate "/../" to "::" , "/./" to ":" and "/" to ":" }
  180. newpos := Length(TranslatePathToMac);
  181. oldlen := Length(path);
  182. SetLength(TranslatePathToMac, newpos + oldlen); {It will be no longer than what is already}
  183. {prepended plus length of path.}
  184. maxpos := Length(TranslatePathToMac); {Get real maxpos, can be short if String is ShortString}
  185. {There is never a slash in the beginning, because either it was an absolute path, and then the}
  186. {drive and slash was removed, or it was a relative path without a preceding slash.}
  187. while oldpos <= oldlen do
  188. begin
  189. {Check if special dirs, ./ or ../ }
  190. if path[oldPos] = '.' then
  191. if (oldpos + 1 <= oldlen) and (path[oldPos + 1] = '.') then
  192. begin
  193. if (oldpos + 2 > oldlen) or (path[oldPos + 2] in ['/', '\']) then
  194. begin
  195. {It is "../" or ".." translates to ":" }
  196. if newPos = maxPos then
  197. begin {Shouldn't actually happen, but..}
  198. Exit('');
  199. end;
  200. newPos := newPos + 1;
  201. TranslatePathToMac[newPos] := ':';
  202. oldPos := oldPos + 3;
  203. continue; {Start over again}
  204. end;
  205. end
  206. else if (oldpos + 1 > oldlen) or (path[oldPos + 1] in ['/', '\']) then
  207. begin
  208. {It is "./" or "." ignor it }
  209. oldPos := oldPos + 2;
  210. continue; {Start over again}
  211. end;
  212. {Collect file or dir name}
  213. while (oldpos <= oldlen) and not (path[oldPos] in ['/', '\']) do
  214. begin
  215. if newPos = maxPos then
  216. begin {Shouldn't actually happen, but..}
  217. Exit('');
  218. end;
  219. newPos := newPos + 1;
  220. TranslatePathToMac[newPos] := path[oldPos];
  221. oldPos := oldPos + 1;
  222. end;
  223. {When we come here there is either a slash or we are at the end.}
  224. if (oldpos <= oldlen) then
  225. begin
  226. if newPos = maxPos then
  227. begin {Shouldn't actually happen, but..}
  228. Exit('');
  229. end;
  230. newPos := newPos + 1;
  231. TranslatePathToMac[newPos] := ':';
  232. oldPos := oldPos + 1;
  233. end;
  234. end;
  235. SetLength(TranslatePathToMac, newpos);
  236. end
  237. else if (path = '.') then
  238. TranslatePathToMac := ':'
  239. else if (path = '..') then
  240. TranslatePathToMac := '::'
  241. else
  242. TranslatePathToMac := path; {its a mac path}
  243. end;
  244. {Concats the relative or full path path1 and the relative path path2.}
  245. function ConcatMacPath (path1, path2: string): string;
  246. begin
  247. if Pos(':', path1) = 0 then {its partial}
  248. Insert(':', path1, 1); {because otherwise it would be interpreted}
  249. {as a full path, when path2 is appended.}
  250. if path1[Length(path1)] = ':' then
  251. begin
  252. if path2[1] = ':' then
  253. begin
  254. Delete(path1, Length(path1), 1);
  255. ConcatMacPath := Concat(path1, path2)
  256. end
  257. else
  258. ConcatMacPath := Concat(path1, path2)
  259. end
  260. else
  261. begin
  262. if path2[1] = ':' then
  263. ConcatMacPath := Concat(path1, path2)
  264. else
  265. ConcatMacPath := Concat(path1, ':', path2)
  266. end;
  267. end;
  268. function IsMacFullPath (const path: string): Boolean;
  269. begin
  270. if Pos(':', path) = 0 then {its partial}
  271. IsMacFullPath := false
  272. else if path[1] = ':' then
  273. IsMacFullPath := false
  274. else
  275. IsMacFullPath := true
  276. end;
  277. function IsDirectory (var spec: FSSpec): Boolean;
  278. var
  279. err: OSErr;
  280. paramBlock: CInfoPBRec;
  281. begin
  282. with paramBlock do
  283. begin
  284. ioVRefNum := spec.vRefNum;
  285. ioDirID := spec.parID;
  286. ioNamePtr := @spec.name;
  287. ioFDirIndex := 0;
  288. err := PBGetCatInfoSync(@paramBlock);
  289. if err = noErr then
  290. IsDirectory := (paramBlock.ioFlAttrib and $10) <> 0
  291. else
  292. IsDirectory := false;
  293. end;
  294. end;
  295. {Gives the path for a given file or directory. If parent is true,
  296. a path to the directory, where the file or directory is located,
  297. is returned. Functioning even with System 6.
  298. TODO use AnsiString instead of Mac_Handle}
  299. function FSpGetFullPath (spec: FSSpec; var fullPathHandle: Mac_Handle;
  300. parent: Boolean): OSErr;
  301. var
  302. res: OSErr;
  303. pb: CInfoPBRec;
  304. begin
  305. fullPathHandle:= NewHandle(0); { Allocate a zero-length handle }
  306. if fullPathHandle = nil then
  307. begin
  308. FSpGetFullPath:= MemError;
  309. Exit;
  310. end;
  311. if spec.parID = fsRtParID then { The object is a volume }
  312. begin
  313. if not parent then
  314. begin
  315. { Add a colon to make it a full pathname }
  316. spec.name := Concat(spec.name, ':');
  317. { We're done }
  318. Munger(fullPathHandle, 0, nil, 0, @spec.name[1], Length(spec.name));
  319. res := MemError;
  320. end
  321. else
  322. res := noErr;
  323. end
  324. else
  325. begin
  326. { The object isn't a volume }
  327. { Add the object name }
  328. if not parent then
  329. Munger(fullPathHandle, 0, nil, 0, @spec.name[1], Length(spec.name));
  330. { Get the ancestor directory names }
  331. pb.ioNamePtr := @spec.name;
  332. pb.ioVRefNum := spec.vRefNum;
  333. pb.ioDrParID := spec.parID;
  334. repeat { loop until we have an error or find the root directory }
  335. begin
  336. pb.ioFDirIndex := -1;
  337. pb.ioDrDirID := pb.ioDrParID;
  338. res := PBGetCatInfoSync(@pb);
  339. if res = noErr then
  340. begin
  341. { Append colon to directory name }
  342. spec.name := Concat(spec.name, ':');
  343. { Add directory name to fullPathHandle }
  344. Munger(fullPathHandle, 0, nil, 0, @spec.name[1], Length(spec.name));
  345. res := MemError;
  346. end
  347. end
  348. until not ((res = noErr) and (pb.ioDrDirID <> fsRtDirID));
  349. end;
  350. if res <> noErr then
  351. begin
  352. DisposeHandle(fullPathHandle);
  353. fullPathHandle:= nil;
  354. end;
  355. FSpGetFullPath := res;
  356. end;
  357. function PathArgToFSSpec(s: string; var spec: FSSpec): Integer;
  358. var
  359. err: OSErr;
  360. begin
  361. err:= FSMakeFSSpec(workingDirectorySpec.vRefNum,
  362. workingDirectorySpec.parID, s, spec);
  363. PathArgToFSSpec := MacOSErr2RTEerr(err);
  364. end;
  365. function PathArgToFullPath(s: string; var fullpath: AnsiString): Integer;
  366. var
  367. err: OSErr;
  368. res: Integer;
  369. spec: FSSpec;
  370. pathHandle: Mac_Handle;
  371. begin
  372. res:= PathArgToFSSpec(s, spec);
  373. if (res = 0) or (res = 2) then
  374. begin
  375. err:= FSpGetFullPath(spec, pathHandle, false);
  376. if err = noErr then
  377. begin
  378. HLock(pathHandle);
  379. SetString(fullpath, pathHandle^, GetHandleSize(pathHandle));
  380. DisposeHandle(pathHandle);
  381. PathArgToFullPath:= 0;
  382. end
  383. else
  384. PathArgToFullPath:= MacOSErr2RTEerr(err);
  385. end
  386. else
  387. PathArgToFullPath:=res;
  388. end;
  389. function GetVolumeName(vRefNum: Integer; var volName: String): OSErr;
  390. var
  391. pb: HParamBlockRec;
  392. begin
  393. pb.ioNamePtr := @volName;
  394. pb.ioVRefNum := vRefNum;
  395. pb.ioVolIndex := 0;
  396. PBHGetVInfoSync(@pb);
  397. volName:= volName + ':';
  398. GetVolumeName:= pb.ioResult;
  399. end;
  400. function GetWorkingDirectoryVRefNum: Integer;
  401. begin
  402. GetWorkingDirectoryVRefNum:= workingDirectorySpec.vRefNum;
  403. end;