Windows.HostFiles64.Mod 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  1. MODULE HostFiles64; (*AUTHOR "fof,ejz"; PURPOSE "Windows file system for WinAos"; *)
  2. IMPORT SYSTEM, Machine, Kernel32, KernelLog, Modules, Kernel, Files := Files64, Commands;
  3. CONST
  4. PathDelimiter = "\"; BufferSize = 4096;
  5. (** File flags *)
  6. ReadOnly* = Files.ReadOnly;
  7. Directory* = Files.Directory;
  8. Hidden* = Files.Hidden;
  9. System* = Files.System;
  10. Archive* = Files.Archive;
  11. Temporary* = Files.Temporary;
  12. TraceFile = 0; TraceFileSystem = 1; TraceCollection = 2; TraceSearch = 3; Trace = {};
  13. TraceMounting=FALSE;
  14. deviceArrival* = 08000H; (* DBT_DEVICEARRIVAL = 08000H *)
  15. deviceRemove* = 08004H; (* DBT_DEVICEREMOVECOMPLETE = 08004H *)
  16. TYPE
  17. FileName = ARRAY Kernel32.MaxPath OF CHAR;
  18. PFileName = POINTER TO FileName;
  19. NotificationProc* = PROCEDURE ( type: LONGINT; drives: SET );
  20. Notification = POINTER TO RECORD
  21. p: NotificationProc;
  22. next: Notification
  23. END;
  24. VAR
  25. searchPath: ARRAY 4 * Kernel32.MaxPath OF CHAR;
  26. workPath, tempPath: FileName; notifications: Notification;
  27. TYPE
  28. SearchByName = OBJECT
  29. VAR sname: FileName;
  30. found: File;
  31. PROCEDURE Init( name: ARRAY OF CHAR );
  32. BEGIN
  33. found := NIL; UpperCase( name, sname )
  34. END Init;
  35. PROCEDURE EnumFile( f: ANY; VAR cont: BOOLEAN );
  36. VAR F: File; fname: FileName;
  37. BEGIN
  38. F := f( File ); UpperCase( F.fname, fname );
  39. IF TraceSearch IN Trace THEN KernelLog.String( "Enumerate: " ); KernelLog.String( fname );
  40. END;
  41. IF sname = fname THEN found := F; cont := FALSE ELSE cont := TRUE END;
  42. IF TraceSearch IN Trace THEN
  43. IF cont THEN KernelLog.String( " # " ); ELSE KernelLog.String( " = " ); END;
  44. KernelLog.String( sname ); KernelLog.Ln;
  45. END;
  46. END EnumFile;
  47. END SearchByName;
  48. FinalizeFiles = OBJECT
  49. PROCEDURE EnumFile( f: ANY; VAR cont: BOOLEAN );
  50. VAR F: File;
  51. BEGIN
  52. F := f( File ); F.Finalize(); cont := TRUE
  53. END EnumFile;
  54. END FinalizeFiles;
  55. Collection = OBJECT (* methods in Collection shared by objects Filesystem and File *)
  56. VAR oldFiles, newFiles: Kernel.FinalizedCollection;
  57. search: SearchByName;
  58. fileKey: LONGINT;
  59. PROCEDURE & Init*;
  60. BEGIN
  61. NEW( oldFiles ); NEW( newFiles ); NEW( search ); fileKey := -1;
  62. END Init;
  63. PROCEDURE GetNextFileKey( ): LONGINT;
  64. BEGIN {EXCLUSIVE}
  65. DEC( fileKey ); RETURN fileKey
  66. END GetNextFileKey;
  67. PROCEDURE Register( F: File );
  68. BEGIN {EXCLUSIVE}
  69. IF TraceCollection IN Trace THEN KernelLog.String( "Collections.Register " ); KernelLog.String( F.fname ); KernelLog.Ln; END;
  70. oldFiles.Add( F, FinalizeFile ); newFiles.Remove( F ); DEC( fileKey ); F.Init( F.fname, F.hfile, fileKey,F.fileSystem );
  71. END Register;
  72. PROCEDURE Unregister( F: File );
  73. BEGIN {EXCLUSIVE}
  74. IF TraceCollection IN Trace THEN KernelLog.String( "Unregister " ); KernelLog.String( F.fname ); KernelLog.Ln; END;
  75. oldFiles.Remove( F ); newFiles.Add( F, FinalizeFile (* FinalizeFile*) ); F.Init( F.fname, Kernel32.InvalidHandleValue, 0, F.fileSystem );
  76. END Unregister;
  77. PROCEDURE AddNew( F: File );
  78. BEGIN {EXCLUSIVE}
  79. IF TraceCollection IN Trace THEN KernelLog.String( "Collections.AddNew: " ); KernelLog.String( F.fname ); KernelLog.Ln; END;
  80. newFiles.Add( F, FinalizeFile );
  81. END AddNew;
  82. PROCEDURE AddOld( F: File );
  83. BEGIN {EXCLUSIVE}
  84. IF TraceCollection IN Trace THEN KernelLog.String( "Collections.AddOld: " ); KernelLog.String( F.fname ); KernelLog.Ln; END;
  85. oldFiles.Add( F, FinalizeFile );
  86. END AddOld;
  87. PROCEDURE ByName( VAR fname: ARRAY OF CHAR ): File;
  88. BEGIN {EXCLUSIVE}
  89. IF TraceCollection IN Trace THEN KernelLog.String( "Collections.ByName: " ); KernelLog.String( fname ); KernelLog.Ln; END;
  90. search.Init( fname ); oldFiles.Enumerate( search.EnumFile ); RETURN search.found
  91. END ByName;
  92. PROCEDURE ByNameNotGC( VAR fname: ARRAY OF CHAR ): File;
  93. BEGIN {EXCLUSIVE}
  94. IF TraceCollection IN Trace THEN KernelLog.String( "Collections.ByName: " ); KernelLog.String( fname ); KernelLog.Ln; END;
  95. search.Init( fname ); oldFiles.EnumerateN( search.EnumFile ); RETURN search.found;
  96. END ByNameNotGC;
  97. PROCEDURE Finalize;
  98. VAR fin: FinalizeFiles;
  99. BEGIN {EXCLUSIVE}
  100. IF TraceCollection IN Trace THEN KernelLog.String( "Collections.Finalize " ); KernelLog.Ln; END;
  101. NEW( fin ); newFiles.Enumerate( fin.EnumFile ); newFiles.Clear(); oldFiles.Enumerate( fin.EnumFile ); oldFiles.Clear();
  102. END Finalize;
  103. PROCEDURE FinalizeFile( obj: ANY );
  104. VAR F: File;
  105. BEGIN
  106. F := obj( File );
  107. IF TraceCollection IN Trace THEN KernelLog.String( "Collections.FinalizeFile " ); KernelLog.String( F.fname ); KernelLog.Ln; END;
  108. F.Finalize()
  109. END FinalizeFile;
  110. END Collection;
  111. AliasFileSystem* = OBJECT (Files.FileSystem)
  112. VAR fs: WinFileSystem;
  113. useprefix*: BOOLEAN;
  114. PROCEDURE Prefix( CONST name: ARRAY OF CHAR; VAR res: ARRAY OF CHAR );
  115. BEGIN
  116. IF useprefix & (name # "") THEN Join( prefix, ":", name, res ); ELSE COPY( name, res ); END;
  117. END Prefix;
  118. PROCEDURE & Init*;
  119. BEGIN
  120. SELF.fs := winFS; useprefix := TRUE; INCL( flags, Files.NeedsPrefix );
  121. END Init;
  122. PROCEDURE New0*( name: ARRAY OF CHAR ): Files.File;
  123. VAR fname: FileName; f: Files.File;
  124. BEGIN
  125. Prefix( name, fname ); f := fs.New0( fname ); IF f # NIL THEN f.fs := SELF; END; RETURN f;
  126. END New0;
  127. PROCEDURE Old0*( name: ARRAY OF CHAR ): Files.File;
  128. VAR fname: FileName; f: Files.File;
  129. BEGIN
  130. Prefix( name, fname ); f := fs.Old0( fname ); IF f # NIL THEN f.fs := SELF; END; RETURN f;
  131. END Old0;
  132. PROCEDURE Delete0*( name: ARRAY OF CHAR; VAR key, res: LONGINT );
  133. VAR fname: FileName;
  134. BEGIN
  135. Prefix( name, fname ); fs.Delete0( fname, key, res );
  136. END Delete0;
  137. PROCEDURE Rename0*( old, new: ARRAY OF CHAR; fold: Files.File; VAR res: WORD );
  138. VAR old0, new0: FileName;
  139. BEGIN
  140. Prefix( old, old0 ); Prefix( new, new0 ); fs.Rename0( old0, new0, fold, res );
  141. END Rename0;
  142. PROCEDURE Enumerate0*( mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator );
  143. VAR fmask: FileName;
  144. BEGIN
  145. Prefix( mask, fmask ); fs.Enumerate1( fmask, flags, enum, useprefix );
  146. END Enumerate0;
  147. PROCEDURE FileKey*( name: ARRAY OF CHAR ): LONGINT;
  148. VAR fname: FileName;
  149. BEGIN
  150. Prefix( name, fname ); RETURN fs.FileKey( fname );
  151. END FileKey;
  152. PROCEDURE CreateDirectory0*( name: ARRAY OF CHAR; VAR res: WORD );
  153. VAR fname: FileName;
  154. BEGIN
  155. Prefix( name, fname ); fs.CreateDirectory0( fname, res );
  156. END CreateDirectory0;
  157. PROCEDURE RemoveDirectory0*( name: ARRAY OF CHAR; force: BOOLEAN; VAR key, res: LONGINT );
  158. VAR fname: FileName;
  159. BEGIN
  160. Prefix( name, fname ); fs.RemoveDirectory0( fname, force, key, res );
  161. END RemoveDirectory0;
  162. PROCEDURE Has*(CONST name: ARRAY OF CHAR; VAR fullName: ARRAY OF CHAR; VAR flags: SET): BOOLEAN;
  163. VAR fname: FileName;
  164. BEGIN
  165. Prefix(name, fname ); RETURN fs.Has(fname, fullName, flags);
  166. END Has;
  167. END AliasFileSystem;
  168. WinFileSystem = OBJECT (* own object for synchronisation of all actions on the (unique) windows file system *)
  169. VAR collection: Collection;
  170. PROCEDURE & Init*;
  171. BEGIN
  172. NEW( collection );
  173. END Init;
  174. PROCEDURE New0( name: ARRAY OF CHAR ): Files.File;
  175. VAR F: File; fname: FileName;
  176. BEGIN {EXCLUSIVE}
  177. ConvertChar( name, Files.PathDelimiter, PathDelimiter );
  178. IF TraceFileSystem IN Trace THEN KernelLog.String( "New0 " ); KernelLog.String( name ); KernelLog.Ln; END;
  179. F := NIL;
  180. IF name = "" THEN (* temporary file *)
  181. NEW( F, name, Kernel32.InvalidHandleValue, 0, SELF ); collection.AddNew( F );
  182. ELSIF FullPathName( name, fname ) & CheckPath(fname) THEN
  183. NEW( F, fname, Kernel32.InvalidHandleValue, 0, SELF ); collection.AddNew( F );
  184. END;
  185. IF TraceFileSystem IN Trace THEN KernelLog.String( "failed" ); KernelLog.Ln; END;
  186. RETURN F;
  187. END New0;
  188. PROCEDURE Old0( name: ARRAY OF CHAR ): Files.File;
  189. VAR F: File; hfile: Kernel32.HANDLE; fname: FileName;
  190. BEGIN {EXCLUSIVE}
  191. ConvertChar( name, Files.PathDelimiter, PathDelimiter );
  192. IF TraceFileSystem IN Trace THEN KernelLog.String( "Old0 " ); KernelLog.String( name ); KernelLog.Ln; END;
  193. IF (name # "") & FindFile( name, fname ) THEN
  194. hfile := Kernel32.CreateFile( fname, {Kernel32.GenericRead}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.OpenExisting, {Kernel32.FileAttributeNormal}, 0 );
  195. IF hfile # Kernel32.InvalidHandleValue THEN NEW( F, fname, hfile, collection.GetNextFileKey() , SELF); collection.AddOld( F ); RETURN F END
  196. END;
  197. IF TraceFileSystem IN Trace THEN KernelLog.String( "failed" ); KernelLog.Ln; END;
  198. RETURN NIL
  199. END Old0;
  200. PROCEDURE Delete0( name: ARRAY OF CHAR; VAR key, res: LONGINT );
  201. VAR fname: FileName; F: File; ret: Kernel32.BOOL;
  202. BEGIN {EXCLUSIVE}
  203. ConvertChar( name, Files.PathDelimiter, PathDelimiter ); key := 0; res := 1;
  204. IF FullPathName( name, fname ) THEN
  205. F := collection.ByName( fname );
  206. IF F # NIL THEN
  207. key := F.key;
  208. IF F.ToTemp() THEN res := 0 END;
  209. ELSE
  210. ret := Kernel32.DeleteFile( fname );
  211. IF ret # 0 THEN res := 0 END
  212. END
  213. END
  214. END Delete0;
  215. PROCEDURE Rename0( old, new: ARRAY OF CHAR; fold: Files.File; VAR res: WORD );
  216. VAR fnold, fnnew: FileName; Fo, Fn: File; ret: Kernel32.BOOL;
  217. BEGIN {EXCLUSIVE}
  218. IF TraceFileSystem IN Trace THEN KernelLog.String( "Rename " ); KernelLog.String( old ); KernelLog.String( " -> " ); KernelLog.String( new ); KernelLog.Ln; END;
  219. ConvertChar( old, Files.PathDelimiter, PathDelimiter ); ConvertChar( new, Files.PathDelimiter, PathDelimiter ); res := 1;
  220. IF FullPathName( old, fnold ) & FullPathName( new, fnnew ) THEN
  221. Fn := collection.ByName( fnnew );
  222. IF Fn # NIL THEN
  223. IF ~Fn.ToTemp() THEN RETURN END
  224. END;
  225. IF fold # NIL THEN
  226. Fo := fold( File );
  227. IF ~Fo.ToTemp() THEN RETURN END;
  228. ret := Kernel32.CopyFile( Fo.tfname^, fnnew, 0 )
  229. ELSE ret := Kernel32.MoveFileEx( fnold, fnnew, {Kernel32.MoveFileReplaceExisting, Kernel32.MoveFileCopyAllowed} )
  230. END;
  231. IF ret # 0 THEN res := 0 END
  232. ELSIF TraceFileSystem IN Trace THEN KernelLog.String( "Rename failed :" ); KernelLog.String( fnold ); KernelLog.String( " => " ); KernelLog.String( fnnew ); KernelLog.Ln;
  233. END
  234. END Rename0;
  235. PROCEDURE Enumerate1( mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator; useprefix: BOOLEAN );
  236. VAR i, j: LONGINT;
  237. path, pattern: ARRAY 256 OF CHAR;
  238. attr: SET; curPath, longname: FileName;
  239. PROCEDURE EnumeratePath;
  240. VAR h: Kernel32.HANDLE; FD: Kernel32.FindData; ft: Kernel32.FileTime; st: Kernel32.SystemTime; i, j, t, d: LONGINT;
  241. BEGIN
  242. i := 0;
  243. WHILE curPath[i] # 0X DO INC( i ) END;
  244. IF curPath[i - 1] # PathDelimiter THEN curPath[i] := PathDelimiter; INC( i ); curPath[i] := 0X END;
  245. j := i - 1; h := 0;
  246. WHILE pattern[h] # 0X DO curPath[i] := pattern[h]; INC( i ); INC( h ) END;
  247. IF h = 0 THEN curPath[i] := "*"; INC( i ); curPath[i] := "."; INC( i ); curPath[i] := "*"; INC( i ) END;
  248. curPath[i] := 0X;
  249. h := Kernel32.FindFirstFile( curPath, FD ); curPath[j] := 0X; ConvertChar( curPath, PathDelimiter, Files.PathDelimiter ); FixDriveLetter (curPath);
  250. IF h # Kernel32.InvalidHandleValue THEN
  251. t := 0; d := 0;
  252. REPEAT
  253. IF Files.EnumTime IN flags THEN
  254. IGNORE Kernel32.FileTimeToLocalFileTime( FD.ftLastWriteTime, ft );
  255. IGNORE Kernel32.FileTimeToSystemTime( ft, st );
  256. d := LONG( st.wYear - 1900 ) * 200H + LONG( st.wMonth ) * 20H + LONG( st.wDay ); t := LONG( st.wHour ) * 1000H + LONG( st.wMinute ) * 40H + LONG( st.wSecond );
  257. END;
  258. Join( curPath, "/", FD.cFileName, longname );
  259. IF ~(Kernel32.FileAttributeDirectory IN FD.dwFileAttributes) THEN
  260. enum.PutEntry( longname, {}, t, d, HUGEINT(UNSIGNED64( FD.nFileSizeHigh ) * UNSIGNED64( 0x100000000 ) + UNSIGNED64( FD.nFileSizeLow )))
  261. ELSIF (FD.cFileName # ".") & (FD.cFileName # "..") THEN
  262. enum.PutEntry( longname, {Files.Directory}, t, d, HUGEINT(UNSIGNED64( FD.nFileSizeHigh ) * UNSIGNED64( 0x100000000 ) + UNSIGNED64( FD.nFileSizeLow )))
  263. END;
  264. UNTIL Kernel32.FindNextFile( h, FD ) = Kernel32.False;
  265. IGNORE Kernel32.FindClose( h )
  266. END;
  267. END EnumeratePath;
  268. BEGIN {EXCLUSIVE}
  269. COPY( mask, path ); ConvertChar( path, Files.PathDelimiter, PathDelimiter ); attr := Kernel32.GetFileAttributes( path ); path := "";
  270. IF (Kernel32.FileAttributeDirectory IN attr) & (~(Kernel32.FileAttributeTemporary IN attr)) THEN COPY( mask, path ); COPY( "*", pattern ); ELSE Files.SplitPath( mask, path, pattern ); END;
  271. IF TraceFileSystem IN Trace THEN
  272. KernelLog.String( "Enumerate0: " ); KernelLog.String( mask ); KernelLog.String( " :: " ); KernelLog.String( path ); KernelLog.String( " :: " ); KernelLog.String( pattern ); KernelLog.Ln;
  273. END;
  274. IF enum = NIL THEN RETURN
  275. END;
  276. IF path = "." THEN COPY( workPath, curPath ); EnumeratePath()
  277. ELSIF IsLocalPath(path) THEN
  278. COPY( workPath, curPath );
  279. IF path # "" THEN
  280. ConvertChar(curPath, PathDelimiter, Files.PathDelimiter);
  281. Files.JoinPath(curPath, path, curPath);
  282. ConvertChar( curPath, Files.PathDelimiter, PathDelimiter );
  283. END;
  284. EnumeratePath();
  285. IF ~useprefix THEN
  286. i := 0; j := 0;
  287. WHILE searchPath[i] # 0X DO
  288. IF searchPath[i] # ";" THEN curPath[j] := searchPath[i]; INC( j )
  289. ELSIF j > 0 THEN
  290. curPath[j] := 0X;
  291. IF curPath # workPath THEN
  292. IF path # "" THEN
  293. ConvertChar(curPath, PathDelimiter, Files.PathDelimiter);
  294. Files.JoinPath(curPath, path, curPath);
  295. ConvertChar( curPath, Files.PathDelimiter, PathDelimiter );
  296. END;
  297. EnumeratePath()
  298. END;
  299. j := 0
  300. END;
  301. INC( i )
  302. END;
  303. IF j > 0 THEN
  304. curPath[j] := 0X;
  305. IF path # "" THEN
  306. ConvertChar(curPath, PathDelimiter, Files.PathDelimiter);
  307. Files.JoinPath(curPath, path, curPath);
  308. ConvertChar( curPath, Files.PathDelimiter, PathDelimiter );
  309. END;
  310. IF curPath # workPath THEN EnumeratePath() END
  311. END;
  312. END;
  313. ELSE (* path is an absolute path *)
  314. COPY( path, curPath ); ConvertChar( curPath, Files.PathDelimiter, PathDelimiter ); EnumeratePath()
  315. END;
  316. END Enumerate1;
  317. PROCEDURE FileKey( name: ARRAY OF CHAR ): LONGINT;
  318. VAR fname: FileName; F: File;
  319. BEGIN {EXCLUSIVE}
  320. (*RETURN 0; (* Finalizers may steal file *) *)
  321. IF name = "" THEN RETURN 0 END;
  322. IF TraceFileSystem IN Trace THEN KernelLog.String( "FileKey " ); KernelLog.String( name ); KernelLog.Ln;
  323. END;
  324. ConvertChar( name, Files.PathDelimiter, PathDelimiter );
  325. IF FindFile( name, fname ) (* OR FullPathName(name,fname) *) THEN
  326. F := collection.ByNameNotGC( fname );
  327. IF F # NIL THEN RETURN F.key END
  328. ELSIF TraceFileSystem IN Trace THEN KernelLog.String( "not found: " ); KernelLog.String( name ); KernelLog.String( "(" ); KernelLog.String( fname ); KernelLog.String( ")" ); KernelLog.Ln;
  329. END;
  330. IF TraceFileSystem IN Trace THEN KernelLog.String( "no key: " ); KernelLog.String( name ); KernelLog.String( "(" ); KernelLog.String( fname ); KernelLog.String( ")" ); KernelLog.Ln; END;
  331. RETURN 0
  332. END FileKey;
  333. PROCEDURE CreateDirectory0( name: ARRAY OF CHAR; VAR res: WORD );
  334. VAR ret: Kernel32.BOOL;
  335. BEGIN {EXCLUSIVE}
  336. ConvertChar( name, Files.PathDelimiter, PathDelimiter ); ret := Kernel32.CreateDirectory( name, NIL );
  337. IF ret # 0 THEN
  338. res := 0;
  339. ELSIF Kernel32.GetLastError() = 183 (*ERROR_ALREADY_EXISTS*) THEN
  340. res := Files.FileAlreadyExists;
  341. ELSE
  342. res := 1
  343. END
  344. END CreateDirectory0;
  345. PROCEDURE RemoveDirectory0( name: ARRAY OF CHAR; force: BOOLEAN; VAR key, res: LONGINT );
  346. VAR ret: Kernel32.BOOL;
  347. BEGIN {EXCLUSIVE}
  348. ConvertChar( name, Files.PathDelimiter, PathDelimiter ); key := 0; res := 1;
  349. IF ~force THEN
  350. ret := Kernel32.RemoveDirectory( name );
  351. IF ret # 0 THEN res := 0 END
  352. ELSE res := -1
  353. END
  354. END RemoveDirectory0;
  355. PROCEDURE Finalize;
  356. BEGIN
  357. collection.Finalize();
  358. END Finalize;
  359. PROCEDURE Has*(CONST fileName: ARRAY OF CHAR; VAR fullName: ARRAY OF CHAR; VAR flags: SET): BOOLEAN;
  360. VAR name: FileName;
  361. BEGIN
  362. COPY(fileName, name);
  363. ConvertChar(name, Files.PathDelimiter, PathDelimiter );
  364. IF FindFile(fileName, fullName) THEN
  365. flags := FileFlags(Kernel32.GetFileAttributes(fullName));
  366. ConvertChar(fullName, PathDelimiter,Files.PathDelimiter);
  367. RETURN TRUE
  368. ELSE
  369. RETURN FALSE
  370. END;
  371. END Has;
  372. END WinFileSystem;
  373. Buffer = POINTER TO RECORD
  374. data: ARRAY BufferSize OF CHAR;
  375. apos: HUGEINT;
  376. len: LONGINT;
  377. dirty: BOOLEAN
  378. END;
  379. File* = OBJECT (Files.File)
  380. VAR fname: FileName;
  381. tfname: PFileName;
  382. hfile: Kernel32.HANDLE;
  383. buffer: Buffer;
  384. fsize, fpos: HUGEINT;
  385. fileSystem: WinFileSystem;
  386. PROCEDURE & Init*( VAR name: ARRAY OF CHAR; hfile: Kernel32.HANDLE; key: LONGINT ; fs: WinFileSystem);
  387. VAR s: WORDSET; res: Kernel32.BOOL;
  388. BEGIN
  389. IF TraceFile IN Trace THEN KernelLog.String( "Init: " ); KernelLog.String( name ); KernelLog.String( " (" ); KernelLog.Int( key, 1 ); KernelLog.String( ")" ); KernelLog.Ln; END;
  390. SELF.key := key; fpos := 0; SELF.hfile := hfile; COPY( name, SELF.fname ); tfname := NIL;
  391. IF hfile # Kernel32.InvalidHandleValue THEN
  392. ASSERT(Kernel32.GetFileSizeEx(hfile, fsize) # Kernel32.False);
  393. s := Kernel32.GetFileAttributes( name );
  394. IF Kernel32.FileAttributeTemporary IN s THEN EXCL( s, Kernel32.FileAttributeTemporary ); res := Kernel32.SetFileAttributes( name, s ); ASSERT( res # 0 ); s := Kernel32.GetFileAttributes( name ) END;
  395. flags := FileFlags( s )
  396. ELSE flags := {Temporary}; fsize := 0
  397. END;
  398. IF buffer = NIL THEN NEW( buffer ); END;
  399. buffer.apos := -1; buffer.len := 0; buffer.dirty := FALSE;
  400. fileSystem := fs
  401. END Init;
  402. PROCEDURE Set*( VAR r: Files.Rider; pos: HUGEINT );
  403. VAR size: HUGEINT;
  404. BEGIN {EXCLUSIVE}
  405. IF hfile # Kernel32.InvalidHandleValue THEN
  406. ASSERT( Kernel32.GetFileSizeEx( hfile, size ) # Kernel32.False ); (* maybe Windows has modified the file since last access, but we ignore file changes once the file rider is set *)
  407. IF size > fsize THEN fsize := size END;
  408. END;
  409. r.eof := FALSE; r.res := 0; r.file := SELF; r.fs := fs;
  410. IF pos < 0 THEN pos := 0
  411. ELSIF pos > fsize THEN pos := fsize
  412. END;
  413. r.apos := pos DIV BufferSize; r.bpos := LONGINT( pos MOD BufferSize )
  414. END Set;
  415. PROCEDURE Pos*( VAR r: Files.Rider ): HUGEINT;
  416. BEGIN
  417. RETURN r.apos * BufferSize + r.bpos
  418. END Pos;
  419. PROCEDURE WriteBuffer;
  420. VAR pos: HUGEINT; n: LONGINT; res, b: Kernel32.BOOL;
  421. BEGIN
  422. ASSERT( buffer.dirty ); ASSERT( buffer.len > 0 );
  423. pos := buffer.apos * BufferSize;
  424. IF hfile = Kernel32.InvalidHandleValue THEN
  425. ASSERT( Temporary IN flags ); NEW( tfname ); TempName( tfname^ );
  426. hfile := Kernel32.CreateFile( tfname^, {Kernel32.GenericRead, Kernel32.GenericWrite}, {Kernel32.FileShareRead}, NIL , Kernel32.CreateAlways, {Kernel32.FileAttributeTemporary}, 0 );
  427. ASSERT( hfile # Kernel32.InvalidHandleValue ); fpos := 0
  428. END;
  429. IF fpos # pos THEN ASSERT( Kernel32.SetFilePointerEx( hfile, pos, fpos , Kernel32.FileBegin ) # Kernel32.False ); END;
  430. res := Kernel32.WriteFile( hfile, buffer.data, buffer.len, n, NIL );
  431. IF (res = Kernel32.False) & ~(ReadOnly IN flags) THEN
  432. res := Kernel32.CloseHandle( hfile );
  433. IF TraceFile IN Trace THEN KernelLog.String( "closed handle of " ); KernelLog.String( fname ); KernelLog.Ln; END;
  434. hfile :=
  435. Kernel32.CreateFile( fname, {Kernel32.GenericRead, Kernel32.GenericWrite}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.OpenExisting, {Kernel32.FileAttributeNormal}, 0 );
  436. ASSERT( hfile # Kernel32.InvalidHandleValue );
  437. ASSERT( Kernel32.SetFilePointerEx( hfile, pos, fpos , Kernel32.FileBegin ) # Kernel32.False );
  438. res := Kernel32.WriteFile( hfile, buffer.data, buffer.len, n, NIL )
  439. END;
  440. ASSERT( (res # Kernel32.False) & (n = buffer.len) );
  441. INC( fpos, n ); buffer.dirty := FALSE
  442. END WriteBuffer;
  443. PROCEDURE ReadBuffer( apos: HUGEINT );
  444. VAR pos: HUGEINT; n: LONGINT; res, b: Kernel32.BOOL;
  445. BEGIN
  446. IF buffer.dirty THEN WriteBuffer() END;
  447. pos := apos * BufferSize;
  448. IF pos >= fsize THEN buffer.apos := apos; buffer.len := 0; RETURN END;
  449. IF fpos # pos THEN
  450. ASSERT( Kernel32.SetFilePointerEx( hfile, pos, fpos , Kernel32.FileBegin ) # Kernel32.False );
  451. IF (fpos # pos) THEN KernelLog.String( "failed to set buffer: " ); KernelLog.String( fname ); KernelLog.Ln END;
  452. ASSERT( fpos = pos )
  453. END;
  454. res := Kernel32.ReadFile( hfile, buffer.data, BufferSize, n, NIL );
  455. IF res = 0 THEN KernelLog.String( "read file did not work for: " ); KernelLog.String( fname ); KernelLog.Ln; END;
  456. ASSERT( res # 0 ); INC( fpos, n ); buffer.apos := apos; buffer.len := n
  457. END ReadBuffer;
  458. PROCEDURE Read*( VAR r: Files.Rider; VAR x: CHAR );
  459. VAR pos: HUGEINT;
  460. BEGIN {EXCLUSIVE}
  461. pos := r.apos * BufferSize + r.bpos;
  462. IF pos < fsize THEN
  463. IF buffer.apos # r.apos THEN ReadBuffer( r.apos ) END;
  464. x := buffer.data[r.bpos]; INC( pos ); r.apos := pos DIV BufferSize; r.bpos := LONGINT( pos MOD BufferSize )
  465. ELSE
  466. x := 0X; r.eof := TRUE
  467. END
  468. END Read;
  469. PROCEDURE ReadBytes*( VAR r: Files.Rider; VAR x: ARRAY OF CHAR; ofs, len: LONGWORD );
  470. VAR pos: HUGEINT; n: LONGWORD;
  471. BEGIN {EXCLUSIVE}
  472. ASSERT( (ofs + len) <= LEN( x ) );
  473. pos := r.apos * BufferSize + r.bpos;
  474. WHILE (len > 0) & (pos < fsize) DO
  475. IF buffer.apos # r.apos THEN ReadBuffer( r.apos ) END;
  476. n := buffer.len - r.bpos;
  477. IF (n > 0) THEN
  478. IF n > len THEN n := len END;
  479. SYSTEM.MOVE( ADDRESSOF( buffer.data[r.bpos] ), ADDRESSOF( x[ofs] ), n );
  480. INC( pos, n ); INC( ofs, n ); DEC( len, n );
  481. r.apos := pos DIV BufferSize; r.bpos := LONGINT( pos MOD BufferSize );
  482. ELSE
  483. pos := fsize;
  484. END;
  485. END;
  486. r.res := len; r.eof := (pos > fsize) OR ((pos = fsize) & (len > 0));
  487. END ReadBytes;
  488. PROCEDURE Write*( VAR r: Files.Rider; x: CHAR );
  489. VAR pos: HUGEINT;
  490. BEGIN {EXCLUSIVE}
  491. pos := r.apos * BufferSize + r.bpos;
  492. IF buffer.apos # r.apos THEN ReadBuffer( r.apos ) END;
  493. buffer.data[r.bpos] := x; INC( pos );
  494. IF (r.bpos + 1) > buffer.len THEN buffer.len := r.bpos + 1 END;
  495. r.apos := pos DIV BufferSize; r.bpos := LONGINT( pos MOD BufferSize );
  496. IF pos > fsize THEN fsize := pos END;
  497. buffer.dirty := TRUE;
  498. END Write;
  499. PROCEDURE WriteBytes*( VAR r: Files.Rider; CONST x: ARRAY OF CHAR; ofs, len: LONGWORD );
  500. VAR pos: HUGEINT; n: LONGINT;
  501. BEGIN {EXCLUSIVE}
  502. IF len = 0 THEN RETURN END;
  503. ASSERT( (len > 0) & ((ofs + len) <= LEN( x )) ); pos := r.apos * BufferSize + r.bpos;
  504. WHILE len > 0 DO
  505. IF buffer.apos # r.apos THEN ReadBuffer( r.apos ) END;
  506. n := BufferSize - r.bpos;
  507. IF n > len THEN n := LONGINT( len ) END; (*! TODO *)
  508. SYSTEM.MOVE( ADDRESSOF( x[ofs] ), ADDRESSOF( buffer.data[r.bpos] ), n );
  509. IF (r.bpos + n) > buffer.len THEN buffer.len := r.bpos + n END;
  510. INC( pos, n ); INC( ofs, n ); DEC( len, n ); r.apos := pos DIV BufferSize; r.bpos := LONGINT( pos MOD BufferSize );
  511. IF pos > fsize THEN fsize := pos END;
  512. buffer.dirty := TRUE
  513. END
  514. END WriteBytes;
  515. PROCEDURE Length*( ): HUGEINT;
  516. BEGIN {EXCLUSIVE}
  517. RETURN fsize
  518. END Length;
  519. PROCEDURE GetDate*( VAR t, d: LONGINT );
  520. VAR ft, lft: Kernel32.FileTime; st: Kernel32.SystemTime; res: Kernel32.BOOL;
  521. BEGIN {EXCLUSIVE}
  522. res := Kernel32.GetFileTime( hfile, NIL , NIL , ft );
  523. (*
  524. ASSERT ( res # 0 ); (* <- only if file is not temporary ! *)
  525. *)
  526. res := Kernel32.FileTimeToLocalFileTime( ft, lft ); res := Kernel32.FileTimeToSystemTime( lft, st ); d := LONG( st.wYear - 1900 ) * 200H + LONG( st.wMonth ) * 20H + LONG( st.wDay );
  527. t := LONG( st.wHour ) * 1000H + LONG( st.wMinute ) * 40H + LONG( st.wSecond )
  528. END GetDate;
  529. PROCEDURE SetDate*( t, d: LONGINT );
  530. VAR ft, lft: Kernel32.FileTime; st: Kernel32.SystemTime; res: Kernel32.BOOL; err: LONGINT;
  531. BEGIN {EXCLUSIVE}
  532. st.wDay := SHORT( d MOD 20H ); d := ASH( d, -5 ); st.wMonth := SHORT( d MOD 10H ); d := ASH( d, -4 ); st.wYear := SHORT( d MOD 80H ) + 1900; st.wMilliseconds := 0;
  533. st.wSecond := SHORT( t MOD 40H ); t := ASH( t, -6 ); st.wMinute := SHORT( t MOD 40H ); t := ASH( t, -6 ); st.wHour := SHORT( t MOD 20H ); res := Kernel32.SystemTimeToFileTime( st, lft );
  534. res := Kernel32.LocalFileTimeToFileTime( lft, ft ); res := Kernel32.SetFileTime( hfile, NIL , NIL , ft );
  535. IF res = 0 THEN err := Kernel32.GetLastError(); DebugFile(SELF) END;
  536. ASSERT( res # 0 )
  537. END SetDate;
  538. PROCEDURE GetAttributes*(): SET;
  539. VAR s: WORDSET;
  540. BEGIN
  541. s := Kernel32.GetFileAttributes( fname );
  542. RETURN FileFlags(s);
  543. END GetAttributes;
  544. PROCEDURE SetAttributes*(a: SET);
  545. VAR s: WORDSET;
  546. BEGIN
  547. s:= WindowsFlags(a);
  548. SetFileAttributes(fname,s);
  549. END SetAttributes;
  550. PROCEDURE GetName*( VAR name: ARRAY OF CHAR );
  551. VAR i: LONGINT; ch: CHAR;
  552. BEGIN {EXCLUSIVE}
  553. COPY( fname, name ); i := 0; ch := name[0];
  554. WHILE ch # 0X DO
  555. IF ch = PathDelimiter THEN name[i] := Files.PathDelimiter END;
  556. INC( i ); ch := name[i]
  557. END
  558. END GetName;
  559. PROCEDURE ToTemp( ): BOOLEAN;
  560. VAR tfname: PFileName; res: Kernel32.BOOL;
  561. from, to: ARRAY 256 OF CHAR;
  562. BEGIN {EXCLUSIVE}
  563. ASSERT( ~(Temporary IN flags) );
  564. (*ALEX 2005.12.08*)
  565. IF hfile = Kernel32.InvalidHandleValue THEN
  566. hfile := Kernel32.CreateFile( fname, {Kernel32.GenericRead}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.CreateAlways, {Kernel32.FileAttributeNormal}, 0 );
  567. END;
  568. IF hfile = Kernel32.InvalidHandleValue THEN
  569. hfile := Kernel32.CreateFile( fname, {Kernel32.GenericRead}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.OpenExisting, {Kernel32.FileAttributeNormal}, 0 );
  570. END;
  571. IF buffer.dirty THEN WriteBuffer() END;
  572. (*
  573. IF hfile # Kernel32.InvalidHandleValue THEN
  574. *)
  575. ASSERT(hfile # Kernel32.InvalidHandleValue);
  576. ASSERT(Kernel32.GetFileSizeEx(hfile, fsize) # Kernel32.False);
  577. res := Kernel32.CloseHandle( hfile );
  578. hfile := Kernel32.InvalidHandleValue;
  579. (*
  580. END;
  581. *)
  582. NEW( tfname ); TempName( tfname^ ); COPY( fname, from ); COPY( tfname^, to );
  583. IF TraceFile IN Trace THEN KernelLog.String( "toTemp: " ); KernelLog.String( fname ); KernelLog.String( " => " ); KernelLog.String( tfname^ ); KernelLog.Ln; END;
  584. IF ~MoveFile( fname, tfname^ ) THEN HALT( 1241 ) (* RETURN FALSE *) END;
  585. winFS.collection.Unregister( SELF );
  586. hfile := Kernel32.CreateFile( tfname^, {Kernel32.GenericRead, Kernel32.GenericWrite}, {Kernel32.FileShareRead}, NIL , Kernel32.OpenExisting, {Kernel32.FileAttributeTemporary}, 0 );
  587. (* IF hfile = Kernel32.InvalidHandleValue THEN RETURN FALSE END; *)
  588. ASSERT( hfile # Kernel32.InvalidHandleValue );
  589. ASSERT( Kernel32.GetFileSizeEx( hfile, fsize ) # Kernel32.False );
  590. SELF.tfname := tfname;
  591. COPY( tfname^, fname );
  592. RETURN TRUE;
  593. END ToTemp;
  594. PROCEDURE Register0*( VAR res: WORD );
  595. VAR F: File; ret: Kernel32.BOOL;
  596. from, to: ARRAY 256 OF CHAR;
  597. BEGIN {EXCLUSIVE}
  598. IF ~(Temporary IN flags) OR (fname = "") THEN res := 1; RETURN END;
  599. IF buffer.dirty THEN WriteBuffer() END;
  600. IF hfile # Kernel32.InvalidHandleValue THEN ret := Kernel32.CloseHandle( hfile ); hfile := Kernel32.InvalidHandleValue END;
  601. IF TraceFile IN Trace THEN KernelLog.String( "Register: existing?: " ); KernelLog.String( fname ); KernelLog.Ln; END;
  602. F := winFS.collection.ByName( fname );
  603. IF (TraceFile IN Trace) & (F = NIL ) THEN KernelLog.String( "Register: not existing: " ); KernelLog.String( fname ); KernelLog.Ln; END;
  604. IF (F # NIL ) THEN
  605. IF ~F.ToTemp() THEN res := 1; RETURN END
  606. END; (* registered file stays alive for its users *)
  607. IF tfname # NIL THEN
  608. COPY( tfname^, from ); COPY( fname, to );
  609. IF ~MoveFile( tfname^, fname ) THEN
  610. (* first try closing file -> for windows versions < 2000 *)
  611. res := 1; RETURN;
  612. (* HALT( 1242 )*)
  613. END;
  614. hfile := Kernel32.CreateFile( fname, {Kernel32.GenericRead, Kernel32.GenericWrite}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.OpenExisting, {Kernel32.FileAttributeNormal}, 0 )
  615. ELSE hfile := Kernel32.CreateFile( fname, {Kernel32.GenericRead, Kernel32.GenericWrite}, {Kernel32.FileShareRead, Kernel32.FileShareWrite}, NIL , Kernel32.CreateAlways, {Kernel32.FileAttributeNormal}, 0 )
  616. END;
  617. IF hfile = Kernel32.InvalidHandleValue THEN res := 1; RETURN END;
  618. ASSERT( hfile # Kernel32.InvalidHandleValue ); winFS.collection.Register( SELF ); res := 0
  619. END Register0;
  620. PROCEDURE Update*;
  621. BEGIN {EXCLUSIVE}
  622. IF buffer.dirty THEN WriteBuffer() END
  623. END Update;
  624. PROCEDURE Finalize*;
  625. VAR res: Kernel32.BOOL;
  626. BEGIN {EXCLUSIVE}
  627. IF TraceFile IN Trace THEN KernelLog.String( "File.Finalize " ); KernelLog.String( fname ); KernelLog.Ln; END;
  628. IF hfile # Kernel32.InvalidHandleValue THEN
  629. IF ~(Temporary IN flags) & buffer.dirty THEN WriteBuffer() END;
  630. res := Kernel32.CloseHandle( hfile ); hfile := Kernel32.InvalidHandleValue;
  631. IF (Temporary IN flags) & (tfname # NIL ) THEN
  632. res := Kernel32.DeleteFile( tfname^ );
  633. (*
  634. KernelLog.String("Deleted: "); KernelLog.String(tfname^); KernelLog.Ln;
  635. IF res = 0 THEN KernelLog.String("failed!"); KernelLog.Ln; END;
  636. *)
  637. (* ASSERT ( res # 0 ) *)
  638. END;
  639. (* ASSERT ( res # 0 ) *)
  640. END
  641. END Finalize;
  642. PROCEDURE Close*;
  643. BEGIN
  644. Finalize;
  645. fileSystem.collection.oldFiles.Remove(SELF);
  646. END Close;
  647. END File;
  648. VAR
  649. winFS: WinFileSystem; (* must be unique *)
  650. PROCEDURE DebugFile(f: File);
  651. BEGIN
  652. KernelLog.String("fname = "); KernelLog.String(f.fname); KernelLog.Ln;
  653. KernelLog.String("tname = "); IF f.tfname # NIL THEN KernelLog.String(f.tfname^) ELSE KernelLog.String("(NIL)") END; KernelLog.Ln;
  654. KernelLog.String("hfile = "); KernelLog.Address(f.hfile); KernelLog.Ln;
  655. KernelLog.String("fsize = "); KernelLog.Int(f.fsize, 1); KernelLog.Ln;
  656. KernelLog.String("fpos = "); KernelLog.Int(f.fpos, 1); KernelLog.Ln;
  657. END DebugFile;
  658. PROCEDURE IsLocalPath(path: ARRAY OF CHAR): BOOLEAN;
  659. VAR prefix, name: Files.FileName;
  660. BEGIN
  661. ConvertChar(path, PathDelimiter, Files.PathDelimiter);
  662. Files.SplitName(path, prefix, name);
  663. RETURN (prefix = "") & (name[0] # "/")
  664. END IsLocalPath;
  665. (* WinAPI functions like e.g. GetCurrentDirectory sometimes yield paths with drive letters in lowercase
  666. which have to be capitalized as they are mapped as filesystems which themselves are case sensitive *)
  667. PROCEDURE FixDriveLetter (VAR path: ARRAY OF CHAR);
  668. BEGIN
  669. IF (LEN (path) >= 2) & (path[0] # 0X) & (path[1] = ':') THEN path[0] := CAP (path[0]) END;
  670. END FixDriveLetter;
  671. PROCEDURE MoveFile( VAR from, to: ARRAY OF CHAR ): BOOLEAN;
  672. BEGIN
  673. IF Kernel32.MoveFileEx( from, to, {Kernel32.MoveFileReplaceExisting, Kernel32.MoveFileCopyAllowed} ) = Kernel32.False THEN
  674. IF Kernel32.CopyFile( from, to, Kernel32.False ) = Kernel32.False THEN
  675. IF TraceFile IN Trace THEN KernelLog.String( "could not copy" ); KernelLog.Ln; END;
  676. RETURN FALSE
  677. ELSE
  678. IF Kernel32.DeleteFile( from ) = Kernel32.False THEN
  679. END;
  680. RETURN TRUE; (* warning: Could not delete file ! *)
  681. END
  682. ELSE RETURN TRUE
  683. END
  684. END MoveFile;
  685. PROCEDURE UpperCase( VAR src, dst: ARRAY OF CHAR );
  686. VAR i: LONGINT; ch: CHAR;
  687. BEGIN
  688. i := 0; ch := src[0];
  689. WHILE ch # 0X DO
  690. IF (ch >= "a") & (ch <= "z") THEN ch := CAP( ch ) END;
  691. dst[i] := ch; INC( i ); ch := src[i]
  692. END;
  693. dst[i] := 0X
  694. END UpperCase;
  695. PROCEDURE TempName( VAR name: ARRAY OF CHAR );
  696. VAR temp: FileName;
  697. pref: ARRAY 4 OF CHAR;
  698. ret: LONGINT;
  699. BEGIN
  700. ret := Kernel32.GetTempPath( LEN( temp ), temp ); ASSERT( ret > 0 ); pref := "Aos"; ret := Kernel32.GetTempFileName( temp, pref, 0, name ); FixDriveLetter (name); ASSERT( ret # 0 )
  701. END TempName;
  702. PROCEDURE FullPathName( name: ARRAY OF CHAR; VAR fname: ARRAY OF CHAR ): BOOLEAN;
  703. VAR i, fp: LONGINT;
  704. BEGIN
  705. i := Kernel32.GetFullPathName( name, Kernel32.MaxPath, fname, fp );
  706. FixDriveLetter (fname); RETURN i > 0
  707. END FullPathName;
  708. (* convert flags from windows file flags to A2 file flags *)
  709. PROCEDURE FileFlags( flags: WORDSET ): SET;
  710. VAR s: SET;
  711. BEGIN
  712. s := {};
  713. IF Kernel32.FileAttributeDirectory IN flags THEN INCL( s, Directory ) END;
  714. IF Kernel32.FileAttributeReadonly IN flags THEN INCL( s, ReadOnly ) END;
  715. IF Kernel32.FileAttributeHidden IN flags THEN INCL( s, Hidden ) END;
  716. IF Kernel32.FileAttributeSystem IN flags THEN INCL( s, System ) END;
  717. IF Kernel32.FileAttributeArchive IN flags THEN INCL( s, Archive ) END;
  718. IF Kernel32.FileAttributeTemporary IN flags THEN INCL( s, Temporary ) END;
  719. RETURN s
  720. END FileFlags;
  721. (* convert flags from A2 file flags to windows file flags *)
  722. PROCEDURE WindowsFlags(flags: SET): WORDSET;
  723. VAR s: WORDSET;
  724. BEGIN
  725. s := {};
  726. IF Directory IN flags THEN INCL( s, Kernel32.FileAttributeDirectory) END;
  727. IF ReadOnly IN flags THEN INCL( s, Kernel32.FileAttributeReadonly ) END;
  728. IF Hidden IN flags THEN INCL( s, Kernel32.FileAttributeHidden) END;
  729. IF System IN flags THEN INCL( s, Kernel32.FileAttributeSystem) END;
  730. IF Archive IN flags THEN INCL( s, Kernel32.FileAttributeArchive) END;
  731. IF Temporary IN flags THEN INCL( s, Kernel32.FileAttributeTemporary) END;
  732. RETURN s
  733. END WindowsFlags;
  734. (** Generate a new file system object. Files.NewVol has volume parameter, Files.Par has mount prefix. *)
  735. PROCEDURE NewFS*(context : Files.Parameters);
  736. VAR fs: AliasFileSystem;
  737. BEGIN
  738. IF (Files.This(context.prefix ) = NIL) THEN
  739. NEW( fs ); fs.vol := context.vol; Files.Add( fs, context.prefix );
  740. ELSE
  741. context.error.String( "HostFiles64: " ); context.error.String( context.prefix ); context.error.String( " already in use" );
  742. context.error.Ln;
  743. END;
  744. END NewFS;
  745. PROCEDURE Join( a1, a2, a3: ARRAY OF CHAR; VAR res: ARRAY OF CHAR );
  746. VAR i, j: LONGINT;
  747. BEGIN
  748. i := 0;
  749. WHILE (a1[i] # 0X) DO res[j] := a1[i]; INC( i ); INC( j ) END;
  750. i := 0;
  751. WHILE (a2[i] # 0X) DO res[j] := a2[i]; INC( i ); INC( j ) END;
  752. i := 0;
  753. WHILE (a3[i] # 0X) DO res[j] := a3[i]; INC( i ); INC( j ) END;
  754. res[j] := 0X
  755. END Join;
  756. (*ALEX 2005.02.10, fof 071008*)
  757. PROCEDURE MountDrive(CONST drive: ARRAY OF CHAR; context : Commands.Context);
  758. VAR
  759. p: Files.Parameters; namebuf1, namebuf2: FileName; size, snum, mlen, sysfl: LONGINT;
  760. res: WORD; prefix: ARRAY 256 OF CHAR;
  761. BEGIN
  762. COPY(drive,prefix);
  763. size := LEN( namebuf1 ); res := Kernel32.GetVolumeInformation( prefix, namebuf1, size, snum, mlen, sysfl, namebuf2, size );
  764. IF res = 0 THEN
  765. IF context# NIL THEN
  766. context.error.String("Not mounted (no volume information): "); context.error.String(prefix); context.error.Ln;
  767. context.error.Update;
  768. END;
  769. ELSE
  770. IF (context = NIL) THEN
  771. NEW(context, NIL, NIL, NIL, NIL, NIL);
  772. END;
  773. NEW(p, context.in, context.arg, context.out, context.error, context.caller);
  774. IF TraceMounting THEN
  775. context.out.String( "Mounting: " ); context.out.String( drive );
  776. context.out.String( " (" ); context.out.String( namebuf1 ); context.out.String( "), fs = " );
  777. context.out.String( namebuf2 ); context.out.Ln;
  778. context.out.Update;
  779. END;
  780. prefix[1] := 0X;
  781. COPY( prefix, p.prefix );
  782. NewFS( p );
  783. END;
  784. END MountDrive;
  785. PROCEDURE AutoMountWindowsLogicalDrives( drives: SET );
  786. (* fof 090221
  787. implemented asynchronously as it blocked execution on A2 startup for a while;
  788. now some of the drives may get mounted later in the system,
  789. should not be a problem since the search path is handled through windows anyway
  790. *)
  791. VAR
  792. AutoMountObject: OBJECT
  793. VAR prefix: ARRAY 4 OF CHAR; i: LONGINT; drives: SET;
  794. PROCEDURE & Init(drives:SET);
  795. BEGIN
  796. SELF.drives := drives
  797. END Init;
  798. BEGIN {ACTIVE}
  799. FOR i := 0 TO MAX( SET ) - 1 DO
  800. IF i IN drives THEN
  801. prefix := "X:\"; prefix[0] := CHR( ORD( "A" ) + i );
  802. MountDrive(prefix, NIL);
  803. END;
  804. END;
  805. END;
  806. BEGIN
  807. NEW(AutoMountObject,drives);
  808. END AutoMountWindowsLogicalDrives;
  809. PROCEDURE UnmountDrive(CONST drive: ARRAY OF CHAR; context : Commands.Context);
  810. VAR this: Files.FileSystem;
  811. BEGIN
  812. this := Files.This( drive );
  813. IF (this # NIL ) & (this IS AliasFileSystem) THEN
  814. IF (context # NIL) THEN
  815. context.out.String( "Auto Unmount: " ); context.out.String( drive );
  816. context.out.String( ":" ); context.out.Ln;
  817. ELSE
  818. KernelLog.String("Auto Unmount: "); KernelLog.String(drive); KernelLog.String(":"); KernelLog.Ln;
  819. END;
  820. Files.Remove( this );
  821. END;
  822. END UnmountDrive;
  823. PROCEDURE AutoUnmountLogicalDrives( drives: SET );
  824. VAR i: LONGINT;
  825. prefix: ARRAY 4 OF CHAR;
  826. BEGIN
  827. FOR i := 0 TO MAX( SET ) - 1 DO
  828. IF i IN drives THEN
  829. prefix[0] := CHR( ORD( "A" ) + i ); prefix[1] := 0X;
  830. UnmountDrive(prefix, NIL);
  831. END;
  832. END;
  833. END AutoUnmountLogicalDrives;
  834. PROCEDURE Finalization;
  835. VAR ft: Files.FileSystemTable; i: LONGINT;
  836. BEGIN
  837. Files.GetList( ft );
  838. IF ft # NIL THEN
  839. FOR i := 0 TO LEN( ft^ ) - 1 DO
  840. IF ft[i] IS AliasFileSystem THEN Files.Remove( ft[i] ) END
  841. END
  842. END;
  843. winFS.Finalize;
  844. END Finalization;
  845. PROCEDURE FindFile*( name: ARRAY OF CHAR; VAR fullname: ARRAY OF CHAR ): BOOLEAN;
  846. VAR ret: LONGINT; fileName: Kernel32.LPSTR;
  847. BEGIN
  848. ret := Kernel32.SearchPath( workPath, name, NIL , LEN( fullname ), fullname, fileName );
  849. IF (ret <= 0) THEN ret := Kernel32.SearchPath( searchPath, name, NIL , LEN( fullname ), fullname, fileName ) END;
  850. FixDriveLetter (fullname);
  851. RETURN ret > 0;
  852. END FindFile;
  853. PROCEDURE ConvertChar*( VAR name: ARRAY OF CHAR; from, to: CHAR );
  854. VAR i: LONGINT;
  855. BEGIN
  856. i := 0;
  857. WHILE name[i] # 0X DO
  858. IF name[i] = from THEN name[i] := to END;
  859. INC( i )
  860. END
  861. END ConvertChar;
  862. PROCEDURE SetPaths;
  863. VAR ret, i, j, k: LONGINT;
  864. work, files, temp: ARRAY Kernel32.MaxPath OF CHAR;
  865. directories, dirs: ARRAY 4 * Kernel32.MaxPath OF CHAR;
  866. dir, sysPath: FileName;
  867. PROCEDURE SetSysPath(VAR dir: ARRAY OF CHAR);
  868. VAR ch: CHAR; i: LONGINT;
  869. BEGIN
  870. IF (dir[0] = "~") & (dir[1] = PathDelimiter) THEN
  871. IGNORE Kernel32.SetCurrentDirectory( sysPath );
  872. i := 2;
  873. REPEAT ch := dir[i]; dir[i-2] := ch; INC(i) UNTIL ch = 0X;
  874. ELSE
  875. IGNORE Kernel32.SetCurrentDirectory(workPath)
  876. END;
  877. END SetSysPath;
  878. PROCEDURE AddDir;
  879. BEGIN
  880. IF k > 0 THEN
  881. dir[k] := 0X;
  882. IF dir[k - 1] = '"' THEN dir[k - 1] := 0X END;
  883. ConvertChar( dir, Files.PathDelimiter, PathDelimiter );
  884. SetSysPath(dir);
  885. IF Kernel32.SetCurrentDirectory( dir ) # Kernel32.False THEN
  886. IGNORE Kernel32.GetCurrentDirectory( LEN( dir ), dir );
  887. searchPath[i] := ";"; INC( i ); k := 0;
  888. WHILE dir[k] # 0X DO searchPath[i] := dir[k]; INC( i ); INC( k ) END
  889. END;
  890. k := 0
  891. END
  892. END AddDir;
  893. BEGIN {EXCLUSIVE}
  894. Machine.GetConfig( "Paths.Files", files ); Machine.GetConfig( "Paths.Search", directories );
  895. Machine.GetConfig( "Paths.Temp", temp ); Machine.GetConfig( "Paths.Work", work );
  896. IGNORE Kernel32.GetCurrentDirectory( LEN( workPath ), workPath ); i := 0; ret := 0;
  897. IF files # "" THEN
  898. COPY( files, sysPath );
  899. IF Kernel32.SetCurrentDirectory( sysPath ) # Kernel32.False THEN ret := Kernel32.GetCurrentDirectory( LEN( sysPath ), sysPath ) END
  900. END;
  901. IF ret = 0 THEN
  902. IGNORE Kernel32.GetModuleFileName( Kernel32.hInstance, sysPath, LEN( sysPath ) ); j := -1;
  903. WHILE sysPath[i] # 0X DO
  904. IF sysPath[i] = PathDelimiter THEN j := i END;
  905. INC( i )
  906. END;
  907. i := j + 1; sysPath[i] := 0X; COPY( sysPath, searchPath )
  908. ELSE
  909. WHILE sysPath[i] # 0X DO searchPath[i] := sysPath[i]; INC( i ) END;
  910. searchPath[i] := 0X
  911. END;
  912. COPY( directories, dirs );
  913. IF dirs[0] = '"' THEN j := 1 ELSE j := 0 END;
  914. k := 0;
  915. WHILE dirs[j] # 0X DO
  916. IF (dirs[j] = ";") OR (dirs[j] < " ") THEN AddDir() ELSE dir[k] := dirs[j]; INC( k ) END;
  917. INC( j )
  918. END;
  919. AddDir(); searchPath[i] := 0X; ret := 0;
  920. COPY( temp, tempPath );
  921. IF tempPath # "" THEN
  922. ConvertChar( tempPath, Files.PathDelimiter, PathDelimiter );
  923. SetSysPath(tempPath);
  924. IF Kernel32.SetCurrentDirectory( tempPath ) # Kernel32.False THEN ret := Kernel32.GetCurrentDirectory( LEN( tempPath ), tempPath ) END
  925. END;
  926. IF ret = 0 THEN IGNORE Kernel32.GetTempPath( LEN( tempPath ), tempPath ) END;
  927. COPY( work, dir );
  928. IF dir # "" THEN
  929. ConvertChar( dir, Files.PathDelimiter, PathDelimiter );
  930. SetSysPath(dir);
  931. IF Kernel32.SetCurrentDirectory( dir ) # Kernel32.False THEN IGNORE Kernel32.GetCurrentDirectory( LEN( workPath ), workPath ) END
  932. END;
  933. IGNORE Kernel32.SetCurrentDirectory( workPath );
  934. END SetPaths;
  935. PROCEDURE SameName*( VAR a, b: ARRAY OF CHAR ): BOOLEAN; (** non-portable *)
  936. VAR i, j: LONGINT;
  937. BEGIN
  938. i := 0; j := 0;
  939. WHILE (a[i] # 0X) & (b[j] # 0X) & (CAP( a[i] ) = CAP( b[j] )) DO INC( i ); INC( j ) END;
  940. RETURN (a[i] = 0X) & (b[j] = 0X)
  941. END SameName;
  942. PROCEDURE CheckPath(fullName: ARRAY OF CHAR ): BOOLEAN;
  943. VAR i, j: LONGINT; done: BOOLEAN;
  944. BEGIN
  945. i := 0; j := -1;
  946. WHILE fullName[i] # 0X DO
  947. IF fullName[i] = PathDelimiter THEN j := i END;
  948. INC( i )
  949. END;
  950. IF j > 0 THEN fullName[j] := 0X END;
  951. BEGIN {EXCLUSIVE}
  952. done := Kernel32.SetCurrentDirectory( fullName ) # Kernel32.False;
  953. IGNORE Kernel32.SetCurrentDirectory( workPath ); RETURN done
  954. END;
  955. END CheckPath;
  956. PROCEDURE CheckName*( name: ARRAY OF CHAR ): BOOLEAN;
  957. VAR fullName: FileName; fileNamePart: Kernel32.LPSTR; ret, i: SIZE; ch: CHAR; stream, ok: BOOLEAN;
  958. BEGIN
  959. ConvertChar( name, Files.PathDelimiter, PathDelimiter ); ret := Kernel32.GetFullPathName( name, Kernel32.MaxPath, fullName, fileNamePart );
  960. IF (ret > 0) & CheckPath( fullName ) & (fileNamePart # Kernel32.NULL) THEN
  961. ok := TRUE; stream := FALSE; i := fileNamePart - ADDRESSOF( fullName ); fullName[i - 1] := 0X; ch := fullName[i];
  962. WHILE (ch # 0X) & ok DO
  963. IF ch = ":" THEN
  964. IF stream THEN ok := FALSE ELSE stream := TRUE END
  965. ELSIF (ch = ":") OR (ch = "\") OR (ch = "?") OR (ch = "|") OR (ch = ">") OR (ch = "<") OR (ch = "/") OR (ch = "*") OR (ch = '"') THEN ok := FALSE;
  966. END;
  967. (* \ / : * ? " < > | *)
  968. INC( i ); ch := fullName[i]
  969. END
  970. ELSE ok := FALSE
  971. END;
  972. RETURN ok
  973. END CheckName;
  974. PROCEDURE GetAttributes*( file: ARRAY OF CHAR ): WORDSET; (** non-portable *)
  975. VAR attrs: WORDSET;
  976. BEGIN
  977. ConvertChar( file, Files.PathDelimiter, PathDelimiter ); attrs := Kernel32.GetFileAttributes( file );
  978. IF attrs = {0..31} THEN RETURN {} ELSE RETURN attrs END
  979. END GetAttributes;
  980. PROCEDURE SetAttributes*( file: ARRAY OF CHAR; attrs: WORDSET ); (** non-portable *)
  981. BEGIN
  982. ConvertChar( file, Files.PathDelimiter, PathDelimiter );
  983. IGNORE Kernel32.SetFileAttributes( file, attrs )
  984. END SetAttributes;
  985. PROCEDURE SetFileAttributes*( file: ARRAY OF CHAR; attrs: WORDSET ); (** non-portable *)
  986. BEGIN
  987. ConvertChar( file, Files.PathDelimiter, PathDelimiter );
  988. IGNORE Kernel32.SetFileAttributes( file, attrs )
  989. END SetFileAttributes;
  990. (** Get the current directory. *)
  991. PROCEDURE GetWorkingDirectory*( VAR path: ARRAY OF CHAR );
  992. BEGIN {EXCLUSIVE}
  993. IGNORE Kernel32.GetCurrentDirectory( Kernel32.MaxPath, workPath ); COPY( workPath, path ); ConvertChar( path, PathDelimiter, Files.PathDelimiter ); FixDriveLetter (path);
  994. END GetWorkingDirectory;
  995. (** Change to directory path. *)
  996. PROCEDURE ChangeDirectory*( path: ARRAY OF CHAR; VAR done: BOOLEAN );
  997. BEGIN {EXCLUSIVE}
  998. ConvertChar( path, Files.PathDelimiter, PathDelimiter ); done := Kernel32.SetCurrentDirectory( path ) # Kernel32.False; IGNORE Kernel32.GetCurrentDirectory( Kernel32.MaxPath, workPath );
  999. END ChangeDirectory;
  1000. (** Get the directory for temporary files. *)
  1001. PROCEDURE GetTempDirectory*( VAR path: ARRAY OF CHAR );
  1002. BEGIN
  1003. COPY( tempPath, path ); ConvertChar( path, PathDelimiter, Files.PathDelimiter )
  1004. END GetTempDirectory;
  1005. (** Compute the relative filename (relative to the working directory). *)
  1006. PROCEDURE RelFileName*( fileName: ARRAY OF CHAR; VAR relFileName: ARRAY OF CHAR );
  1007. VAR i, j, k, p: LONGINT; fullName: FileName; fileNamePart: Kernel32.LPSTR;
  1008. BEGIN
  1009. IF ~FindFile( fileName, fullName ) THEN (* file does not exist -> would be created in the current dir *)
  1010. ConvertChar( fileName, Files.PathDelimiter, PathDelimiter );
  1011. IGNORE Kernel32.GetFullPathName( fileName, Kernel32.MaxPath, fullName, fileNamePart ); FixDriveLetter (fullName);
  1012. ELSE ConvertChar( fullName, Files.PathDelimiter, PathDelimiter )
  1013. END; (* from here on all with PathDelimiter and drive letter *)
  1014. IF CAP( workPath[0] ) # CAP( fullName[0] ) THEN (* different drive letters -> nothing to be done *)
  1015. COPY( fullName, relFileName )
  1016. ELSE
  1017. i := 0; j := -1; p := 0;
  1018. WHILE CAP( fullName[i] ) = CAP( workPath[i] ) DO
  1019. IF workPath[i] = PathDelimiter THEN j := i END;
  1020. INC( i )
  1021. END;
  1022. IF workPath[i] = 0X THEN
  1023. IF fullName[i] # PathDelimiter THEN (* first part of directories do match *)
  1024. relFileName[p] := "."; relFileName[p + 1] := "."; relFileName[p + 2] := PathDelimiter; INC( p, 3 ); INC( j );
  1025. WHILE fullName[j] # 0X DO relFileName[p] := fullName[j]; INC( j ); INC( p ) END
  1026. ELSE (* file is in a subdirectory of the current dir *)
  1027. INC( i );
  1028. WHILE fullName[i] # 0X DO relFileName[p] := fullName[i]; INC( i ); INC( p ) END
  1029. END
  1030. ELSIF j > 2 THEN (* first part of directories do match *)
  1031. k := j; i := j + 1;
  1032. WHILE workPath[k] # 0X DO
  1033. IF workPath[k] = PathDelimiter THEN relFileName[p] := "."; relFileName[p + 1] := "."; relFileName[p + 2] := PathDelimiter; INC( p, 3 ) END;
  1034. INC( k )
  1035. END;
  1036. WHILE fullName[i] # 0X DO relFileName[p] := fullName[i]; INC( i ); INC( p ) END
  1037. ELSE (* only drive letters match *)
  1038. i := j;
  1039. WHILE fullName[i] # 0X DO relFileName[p] := fullName[i]; INC( i ); INC( p ) END
  1040. END;
  1041. relFileName[p] := 0X
  1042. END;
  1043. ConvertChar( relFileName, PathDelimiter, Files.PathDelimiter )
  1044. END RelFileName;
  1045. PROCEDURE DeviceNotification*( type: LONGINT; drives: SET );
  1046. VAR n: Notification;
  1047. BEGIN
  1048. IF type = deviceArrival THEN AutoMountWindowsLogicalDrives( drives );
  1049. ELSIF type = deviceRemove THEN AutoUnmountLogicalDrives( drives );
  1050. ELSE
  1051. (* scan for changes *)
  1052. END;
  1053. n := notifications;
  1054. WHILE(n#NIL) DO
  1055. n.p(type,drives);
  1056. n := n.next;
  1057. END;
  1058. END DeviceNotification;
  1059. PROCEDURE RegisterNotification*( p: NotificationProc );
  1060. VAR n: Notification;
  1061. BEGIN
  1062. NEW( n ); n.p := p; n.next := notifications; notifications := n;
  1063. END RegisterNotification;
  1064. PROCEDURE Init;
  1065. VAR
  1066. i, j: LONGINT; sysPath: FileName; p: Files.Parameters; drives: SET; fs : Files.FileSystem;
  1067. BEGIN
  1068. NEW( winFS );
  1069. NEW( p, NIL, NIL, NIL, NIL, NIL); p.prefix := "searcher";
  1070. NewFS( p );
  1071. fs := Files.This(p.prefix);
  1072. IF (fs # NIL) & (fs IS AliasFileSystem) THEN
  1073. fs( AliasFileSystem ).useprefix := FALSE;
  1074. EXCL( fs( AliasFileSystem ).flags, Files.NeedsPrefix );
  1075. END;
  1076. (* now the file system is installed *)
  1077. drives := Kernel32.GetLogicalDrives();
  1078. drives := drives - {0,1}; (* do not scan for diskettes *)
  1079. AutoMountWindowsLogicalDrives( drives );
  1080. IGNORE Kernel32.GetCurrentDirectory( LEN( workPath ), workPath );
  1081. i := 0;
  1082. IGNORE Kernel32.GetModuleFileName( Kernel32.hInstance, sysPath, LEN( sysPath ) ); j := -1;
  1083. FixDriveLetter (workPath); FixDriveLetter (sysPath);
  1084. WHILE sysPath[i] # 0X DO
  1085. IF sysPath[i] = PathDelimiter THEN j := i END;
  1086. INC( i )
  1087. END;
  1088. i := j + 1; sysPath[i] := 0X; COPY( sysPath, searchPath );
  1089. IGNORE Kernel32.GetTempPath( LEN( tempPath ), tempPath );
  1090. IGNORE Kernel32.SetCurrentDirectory( workPath );
  1091. notifications := NIL;
  1092. END Init;
  1093. PROCEDURE AddSearchPath*(context: Commands.Context);
  1094. VAR name,fullName: FileName; i,j: LONGINT; ch : CHAR; ret: LONGINT; fileNamePart: Kernel32.LPSTR;
  1095. BEGIN
  1096. IF context.arg.GetString(name) THEN
  1097. i := 0; j := 0;
  1098. ConvertChar( name, Files.PathDelimiter, PathDelimiter );
  1099. ret := Kernel32.GetFullPathName( name, Kernel32.MaxPath, fullName, fileNamePart );
  1100. WHILE(searchPath[i] # 0X) DO
  1101. INC(i);
  1102. END;
  1103. searchPath[i] := ";";INC(i);
  1104. REPEAT
  1105. ch := fullName[j];
  1106. searchPath[i] := ch;
  1107. INC(j);INC(i);
  1108. UNTIL ch = 0X;
  1109. END;
  1110. END AddSearchPath;
  1111. PROCEDURE SetWorkPath*(context: Commands.Context);
  1112. VAR name: FileName; done: BOOLEAN;
  1113. BEGIN
  1114. IF context.arg.GetString(name) THEN
  1115. ChangeDirectory(name, done);
  1116. IF ~done THEN
  1117. context.error.String("could not change directory to "); context.error.String(name); context.error.Ln;
  1118. END
  1119. END;
  1120. END SetWorkPath;
  1121. PROCEDURE Mount*(context : Commands.Context);
  1122. VAR diskname: ARRAY 256 OF CHAR;
  1123. BEGIN
  1124. context.arg.SkipWhitespace;
  1125. context.arg.String(diskname);
  1126. MountDrive(diskname, context);
  1127. END Mount;
  1128. PROCEDURE Unmount*(context : Commands.Context);
  1129. VAR diskname: ARRAY 256 OF CHAR;
  1130. BEGIN
  1131. context.arg.SkipWhitespace;
  1132. context.arg.String(diskname);
  1133. UnmountDrive(diskname, context);
  1134. END Unmount;
  1135. BEGIN
  1136. Init(); Modules.InstallTermHandler( Finalization ); SetPaths;
  1137. END HostFiles64.