2
0

Win32.WinFiles64.Mod 47 KB

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