Files64.Mod 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE Files64; (* pjm *)
  3. (** Aos file system base. *)
  4. IMPORT SYSTEM, Files, Streams := Streams64, KernelLog, Modules, Kernel, Commands;
  5. CONST
  6. (** Volume & file flags *)
  7. ReadOnly* = Files.ReadOnly;
  8. (** Volume flags *)
  9. Removable* = Files.Removable;
  10. Boot* = Files.Boot;
  11. (** File flags *)
  12. Directory* = Files.Directory;
  13. Hidden* = Files.Hidden;
  14. System* = Files.System;
  15. Archive* = Files.Archive;
  16. Temporary* = Files.Temporary;
  17. Ok* = Files.Ok;
  18. (*
  19. (* Volume level errors *)(* as Files.Mod *)
  20. ReadOnlyError = 2901; (* Tried to modify read-only volume. Causes HALT *)
  21. VolumeFull = 2902; (* Tried to allocate block on full volume. Causes HALT *)
  22. InvalidAdr = 2903; (* Block address outside of volume. Causes HALT *)
  23. *)
  24. (* File level errors *)
  25. VolumeReadOnly* = Files.VolumeReadOnly; (** Cannot modify read-only volume *)
  26. FsNotFound* = Files.FsNotFound; (** File system not found *)
  27. FileAlreadyExists* = Files.FileAlreadyExists; (** File already exists *)
  28. BadFileName* = Files.BadFileName; (** Bad file name *)
  29. FileNotFound* = Files.FileNotFound; (** File not found *)
  30. (* Enumerate flags. *)
  31. EnumSize* = Files.EnumSize;
  32. EnumTime* = Files.EnumTime;
  33. PrefixLength* = Files.PrefixLength; (** maximum length of a file system prefix. *)
  34. NameLength* = Files.NameLength; (** maximum length of a file name. *)
  35. Trace = FALSE;
  36. WriteError = 2907;
  37. DefaultWriterSize = 4096;
  38. DefaultReaderSize = 4096;
  39. PathDelimiter* = "/"; (** Path delimiter *)
  40. BufferSize = 32 * 1024; (* Buffersize for file copy operation *)
  41. SetSize = MAX(SET) + 1;
  42. (* file system behaviour flags *)
  43. NeedsPrefix* = Files.NeedsPrefix; (* if no prefix given, then this file system cannot handle it, to prevent file systems from being in the search path *)
  44. TYPE
  45. Position* = Streams.Position;
  46. TYPE
  47. (** All record fields are read-only for users, and read-write for extenders. *)
  48. FileName* = Files.FileName;
  49. (** A rider points to some location in a file, where reading and writing will be done. *)
  50. Rider* = RECORD (** not shareable between multiple processes *)
  51. (* the rider must be a record, otherwise the Oberon text system will not work *)
  52. eof*: BOOLEAN; (** has end of file been passed *)
  53. res*: Streams.BufferOffset; (** leftover byte count for ReadBytes/WriteBytes *)
  54. (** private fields for implementors *)
  55. apos*: Streams.Position;
  56. bpos*: LONGINT;
  57. hint*: Hint;
  58. file*: File;
  59. fs*: FileSystem;
  60. END;
  61. TYPE
  62. (** Reader for buffered reading of a file via Streams.Read* procedures. See OpenReader. *)
  63. Reader* = OBJECT (Streams.Reader) (** not sharable between multiple processes *)
  64. VAR
  65. file : File;
  66. r: Rider;
  67. PROCEDURE Receive(VAR buf: ARRAY OF CHAR; ofs, size, min: Streams.BufferOffset; VAR len: Streams.BufferOffset; VAR res: WORD);
  68. BEGIN
  69. file.ReadBytes(r, buf, ofs, size);
  70. len := size - r.res;
  71. IF len >= min THEN res := Streams.Ok ELSE res := Streams.EOF (* end of file *) END
  72. END Receive;
  73. PROCEDURE CanSetPos*() : BOOLEAN;
  74. BEGIN
  75. RETURN TRUE;
  76. END CanSetPos;
  77. PROCEDURE SetPos*(pos : Streams.Position);
  78. BEGIN
  79. file.Set(r, pos);
  80. Reset;
  81. received := pos; (* this effects that Streams.Reader.Pos() returns the correct location in the file *)
  82. END SetPos;
  83. PROCEDURE &InitFileReader*(file : File; pos: Streams.Position);
  84. BEGIN
  85. ASSERT(file # NIL);
  86. SELF.file := file;
  87. file.Set(r, pos);
  88. received := pos; (* this effects that Streams.Reader.Pos() returns the correct location in the file *)
  89. InitReader(SELF.Receive, DefaultReaderSize);
  90. END InitFileReader;
  91. END Reader;
  92. TYPE
  93. (** Writer for buffered writing of a file via Streams.Write* procedures. See OpenWriter. *)
  94. Writer* = OBJECT (Streams.Writer) (** not sharable between multiple processes *)
  95. VAR
  96. file : File;
  97. r: Rider;
  98. PROCEDURE Send(CONST buf: ARRAY OF CHAR; ofs, len: Streams.BufferOffset; propagate: BOOLEAN; VAR res: WORD);
  99. BEGIN
  100. r.file.WriteBytes(r, buf, ofs, len);
  101. IF propagate THEN r.file.Update END;
  102. IF r.res = 0 THEN res := Streams.Ok ELSE res := WriteError (* not all bytes written *) END
  103. END Send;
  104. PROCEDURE CanSetPos*() : BOOLEAN;
  105. BEGIN
  106. RETURN TRUE;
  107. END CanSetPos;
  108. PROCEDURE SetPos*(pos : Streams.Position);
  109. BEGIN
  110. Update;
  111. file.Set(r, pos);
  112. Reset;
  113. END SetPos;
  114. PROCEDURE Pos*(): Streams.Position;
  115. BEGIN
  116. Update;
  117. RETURN file.Pos(r)
  118. END Pos;
  119. PROCEDURE &InitFileWriter*(file: File; pos: Streams.Position);
  120. BEGIN
  121. ASSERT(file # NIL);
  122. SELF.file := file;
  123. file.Set(r, pos);
  124. InitWriter(SELF.Send, DefaultWriterSize);
  125. END InitFileWriter;
  126. END Writer;
  127. Prefix* = Files.Prefix;
  128. Hint* = Files.Hint; (** for use by file system implementors. *)
  129. Bytes2 = ARRAY 2 OF CHAR;
  130. Bytes4 = ARRAY 4 OF CHAR;
  131. Bytes8 = ARRAY 8 OF CHAR;
  132. TYPE
  133. (** Volume is the base type of all volumes. It provides operations on an abstract array of file system data blocks of blockSize bytes, numbered from 1 to size. It is mainly used by file system implementations. *)
  134. Volume* = Files.Volume; (** shareable *)
  135. TYPE
  136. FileSystem* = OBJECT (** shareable *)
  137. VAR
  138. next: FileSystem; (* list of known file systems *)
  139. prefix*: Prefix; (** mount prefix *)
  140. desc*: ARRAY 32 OF CHAR; (** description of file system *)
  141. vol*: Volume; (** underlying volume, if any (a boot FS must have a volume) *)
  142. flags*: SET; (* flags like propagate prefix / can process files without prefix *)
  143. (** Create a new file with the specified name. End users use Files.New instead. *)
  144. PROCEDURE New0*(name: ARRAY OF CHAR): File;
  145. BEGIN HALT(301) END New0; (* abstract *)
  146. (** Open an existing file. The same file descriptor is returned if a file is opened multiple times. End users use Files.Old instead. *)
  147. PROCEDURE Old0*(name: ARRAY OF CHAR): File;
  148. BEGIN HALT(301) END Old0; (* abstract *)
  149. (** Delete a file. res = 0 indicates success. End users use Files.Delete instead. *)
  150. PROCEDURE Delete0*(name: ARRAY OF CHAR; VAR key, res: LONGINT);
  151. BEGIN HALT(301) END Delete0; (* abstract *)
  152. (** Rename a file. res = 0 indicates success. End users use Files.Rename instead. *)
  153. PROCEDURE Rename0*(old, new: ARRAY OF CHAR; f: File; VAR res: WORD);
  154. BEGIN HALT(301) END Rename0; (* abstract *)
  155. (** Enumerate canonical file names. mask may contain * wildcards. For internal use only. End users use Enumerator instead. *)
  156. PROCEDURE Enumerate0*(mask: ARRAY OF CHAR; flags: SET; enum: Enumerator);
  157. BEGIN HALT(301) END Enumerate0; (* abstract *)
  158. (** Return the unique non-zero key of the named file, if it exists. *)
  159. PROCEDURE FileKey*(name: ARRAY OF CHAR): LONGINT;
  160. BEGIN HALT(301) END FileKey; (* abstract *)
  161. (** Create a new directory structure. May not be supported by the actual implementation.
  162. End users use Files.CreateDirectory instead.*)
  163. PROCEDURE CreateDirectory0*(name: ARRAY OF CHAR; VAR res: WORD);
  164. BEGIN res := -1 (* not supported *)
  165. END CreateDirectory0;
  166. (** Remove a directory. If force=TRUE, any subdirectories and files should be automatically deleted.
  167. End users use Files.RemoveDirectory instead. *)
  168. PROCEDURE RemoveDirectory0*(name: ARRAY OF CHAR; force: BOOLEAN; VAR key, res: LONGINT);
  169. BEGIN res := -1 (* not supported *)
  170. END RemoveDirectory0;
  171. (** Finalize the file system. *)
  172. PROCEDURE Finalize*;
  173. BEGIN (* see note in Volume.Finalize *)
  174. vol := NIL
  175. END Finalize;
  176. (* default implementation using an enumerator *)
  177. PROCEDURE Has*(CONST name: ARRAY OF CHAR; VAR fullName: ARRAY OF CHAR; VAR flags: SET): BOOLEAN;
  178. VAR enum: Enumerator; time, date: LONGINT; size: Streams.Position;
  179. BEGIN
  180. NEW(enum);
  181. enum.Open(name,{});
  182. IF enum.HasMoreEntries() & enum.GetEntry(fullName, flags, time, date, size) THEN
  183. RETURN TRUE
  184. ELSE
  185. RETURN FALSE
  186. END;
  187. END Has;
  188. (* GC
  189. PROCEDURE Purge*(f: File); (* race! *)
  190. BEGIN HALT(301) END Purge; (* by default not allowed to purge files *)
  191. *)
  192. END FileSystem;
  193. FileSystemTable* = POINTER TO ARRAY OF FileSystem;
  194. TYPE
  195. File* = OBJECT (** sharable *)
  196. VAR
  197. (** private fields for implementors *)
  198. flags*: SET; (** (read-only!) file-specific flags, i.e. Directory. *)
  199. key*: LONGINT; (* unique id for registered file, never 0 *)
  200. fs*: FileSystem; (* file system containing file *)
  201. (** Position a Rider at a certain position in a file. Multiple Riders can be positioned at different locations in a file. A Rider cannot be positioned beyond the end of a file. *)
  202. PROCEDURE Set*(VAR r: Rider; pos: Streams.Position);
  203. BEGIN HALT(301) END Set; (* abstract *)
  204. (** Return the offset of a Rider positioned on a file. *)
  205. PROCEDURE Pos*(VAR r: Rider): Streams.Position;
  206. BEGIN HALT(301) END Pos; (* abstract *)
  207. (** Read a byte from a file, advancing the Rider one byte further. R.eof indicates if the end of the file has been passed. *)
  208. PROCEDURE Read*(VAR r: Rider; VAR x: CHAR);
  209. BEGIN HALT(301) END Read; (* abstract *)
  210. (** Read a sequence of len bytes into the buffer x at offset ofs, advancing the Rider. Less bytes will be read when reading over the end of the file. r.res indicates the number of unread bytes. x must be big enough to hold all the bytes. *)
  211. PROCEDURE ReadBytes*(VAR r: Rider; VAR x: ARRAY OF CHAR; ofs, len: Streams.BufferOffset);
  212. BEGIN HALT(301) END ReadBytes; (* abstract *)
  213. (** Write a byte into the file at the Rider position, advancing the Rider by one. *)
  214. PROCEDURE Write*(VAR r: Rider; x: CHAR);
  215. BEGIN HALT(301) END Write; (* abstract *)
  216. (** Write the buffer x containing len bytes (starting at offset ofs) into a file at the Rider position. *)
  217. PROCEDURE WriteBytes*(VAR r: Rider; CONST x: ARRAY OF CHAR; ofs, len: Streams.BufferOffset);
  218. BEGIN HALT(301) END WriteBytes; (* abstract *)
  219. (** Return the current length of a file. *)
  220. PROCEDURE Length*(): Streams.Position;
  221. BEGIN HALT(301) END Length; (* abstract *)
  222. (** Return the time (t) and date (d) when a file was last modified. *)
  223. PROCEDURE GetDate*(VAR t, d: LONGINT);
  224. BEGIN HALT(301) END GetDate; (* abstract *)
  225. (** Set the modification time (t) and date (d) of a file. *)
  226. PROCEDURE SetDate*(t, d: LONGINT);
  227. BEGIN HALT(301) END SetDate; (* abstract *)
  228. (** Get file attributes. *)
  229. PROCEDURE GetAttributes*(): SET;
  230. BEGIN HALT(301) END GetAttributes; (* abstract *)
  231. (** Set file attributes. *)
  232. PROCEDURE SetAttributes*(flags: SET);
  233. BEGIN HALT(301) END SetAttributes; (* abstract *)
  234. (** Return the canonical name of a file. *)
  235. PROCEDURE GetName*(VAR name: ARRAY OF CHAR);
  236. BEGIN HALT(301) END GetName; (* abstract *)
  237. (** Register a file created with New in the directory, replacing the previous file in the directory with the same name. The file is automatically updated. End users use Files.Register instead. *)
  238. PROCEDURE Register0*(VAR res: WORD);
  239. BEGIN HALT(301) END Register0; (* abstract *)
  240. (** Flush the changes made to a file from its buffers. Register0 will automatically update a file. *)
  241. PROCEDURE Update*;
  242. BEGIN HALT(301) END Update; (* abstract *)
  243. (*
  244. Usually, in Oberon a file is not closed explicitly but by a call to finalizers by the garbage colloector.
  245. However, for systems running in a host environment such as WinAos or UnixAos, explicitly closing a file may release files/handles that otherwise are blocked by the system.
  246. Close is not necessarily implemented by a file system, therefore a call to Close may or may not have any effect.
  247. Implementers of Close have to make sure that a call to close does not possibly contradict with file finalization.
  248. *)
  249. PROCEDURE Close*;
  250. BEGIN
  251. (* abstract, potentially empty *)
  252. END Close;
  253. END File;
  254. TYPE
  255. Enumerator* = OBJECT (** not shareable *)
  256. VAR
  257. r: Rider; (* data is stored in an anonymous file, because it is potentially very long *)
  258. adding: BOOLEAN; (* prevent user calls of PutEntry *)
  259. size-: LONGINT; (** total number of entries *)
  260. (** Open an enumerator and enumerate the files that match mask. *)
  261. PROCEDURE Open*(mask: ARRAY OF CHAR; flags: SET);
  262. BEGIN
  263. r.file := New("");
  264. r.file.Set(r, 0);
  265. size := 0;
  266. adding := TRUE;
  267. Enumerate(mask, flags, SELF);
  268. adding := FALSE;
  269. r.file.Set(r, 0)
  270. END Open;
  271. (** reset the enumerator to the first entry *)
  272. PROCEDURE Reset*;
  273. BEGIN
  274. r.file.Set(r, 0)
  275. END Reset;
  276. (** returns TRUE if the enumerator contains more entries *)
  277. PROCEDURE HasMoreEntries*(): BOOLEAN;
  278. BEGIN
  279. RETURN r.file.Pos(r) < r.file.Length()
  280. END HasMoreEntries;
  281. (** Get one entry from the enumerator. *)
  282. PROCEDURE GetEntry*(VAR name: ARRAY OF CHAR; VAR flags: SET; VAR time, date: LONGINT; VAR size: Streams.Position): BOOLEAN;
  283. VAR len: LONGINT;
  284. BEGIN
  285. ReadNum(r, len);
  286. IF ~r.eof THEN
  287. name[len] := 0X; (* index check *)
  288. r.file.ReadBytes(r, name, 0, len);
  289. ReadSet(r, flags); ReadNum(r, time); ReadNum(r, date); ReadFileSize(r, size);
  290. ASSERT(~r.eof)
  291. END;
  292. RETURN ~r.eof
  293. END GetEntry;
  294. (** Close the enumerator. *)
  295. PROCEDURE Close*;
  296. BEGIN
  297. (*r.fs.Purge(r.file);*)
  298. r.hint := NIL; r.file := NIL; r.fs := NIL
  299. END Close;
  300. (** For internal use only. *)
  301. PROCEDURE PutEntry*(VAR name: ARRAY OF CHAR; flags: SET; time, date: LONGINT; size:Streams.Position);
  302. VAR len: LONGINT;
  303. BEGIN
  304. ASSERT(adding);
  305. INC(SELF.size);
  306. len := 0; WHILE name[len] # 0X DO INC(len) END;
  307. WriteNum(r, len); r.file.WriteBytes(r, name, 0, len);
  308. WriteSet(r, flags); WriteNum(r, time); WriteNum(r, date); WriteFileSize(r, size)
  309. END PutEntry;
  310. END Enumerator;
  311. TYPE
  312. (* FinalizedCollection enumerator searching for a file by (fs,key). *)
  313. FileSearcher = OBJECT
  314. VAR fs: FileSystem; key: LONGINT; found: File;
  315. PROCEDURE EnumFile(f: ANY; VAR cont: BOOLEAN);
  316. BEGIN
  317. WITH f: File DO
  318. IF (f.fs = fs) & (f.key = key) THEN
  319. found := f; cont := FALSE
  320. END
  321. END
  322. END EnumFile;
  323. END FileSearcher;
  324. TYPE
  325. (** Parameters passed to volume and file system generator commands. The str field contains a generic parameter string from the mount command. The vol field returns the new volume from volume generators and passes the volume parameter to file system generators. The prefix field contains the mount prefix, mainly for file system generators to add themselves with Files.Add. *)
  326. Parameters* = Files.Parameters;
  327. FileSystemFactory* = PROCEDURE(context : Parameters);
  328. VAR
  329. fsroot: FileSystem; (* list of known file systems *)
  330. files: Kernel.FinalizedCollection; (* all open files - cleaned up by GC *)
  331. seacher: FileSearcher; (* enumerator shared by various procedures, protected with module EXCLUSIVE *)
  332. fileClipboard : File; (* contains the pointer to the file in the clipboard opened with Copy *)
  333. (** Buffered reading and writing. *)
  334. (** Open a reader on a file at the specified position. *)
  335. PROCEDURE OpenReader*(VAR b: Reader; f: File; pos: Streams.Position);
  336. BEGIN
  337. IF b = NIL THEN
  338. NEW(b, f, pos)
  339. ELSE
  340. b.InitFileReader(f,pos)
  341. END;
  342. END OpenReader;
  343. (** Open a writer on a file at the specified position. Remember to call Streams.Update before registering or closing the file! *)
  344. PROCEDURE OpenWriter*(VAR b: Writer; f: File; pos: Streams.Position);
  345. BEGIN
  346. NEW(b, f, pos)
  347. END OpenWriter;
  348. (** File name prefix support. *)
  349. (** Split fullname = ( prefix ":" name ) into prefix and name *)
  350. PROCEDURE SplitName*(fullname: ARRAY OF CHAR; VAR prefix, name: ARRAY OF CHAR);
  351. VAR i, j, len: SIZE;
  352. BEGIN
  353. i := 0; WHILE (fullname[i] # ":") & (fullname[i] # 0X) DO INC(i) END;
  354. IF (fullname[i] # ":") OR (i >= LEN(prefix)) THEN
  355. COPY("", prefix); COPY(fullname, name);
  356. ELSE
  357. j := 0; WHILE j # i DO prefix[j] := fullname[j]; INC(j) END;
  358. prefix[j] := 0X;
  359. j := 0; INC(i); len := LEN(name)-1;
  360. WHILE (j < len) & (fullname[i] # 0X) DO name[j] := fullname[i]; INC(j); INC(i) END;
  361. name[j] := 0X
  362. END
  363. END SplitName;
  364. (** Join prefix and name to fullname = ( prefix ":" name ) *)
  365. PROCEDURE JoinName*(prefix, name: ARRAY OF CHAR; VAR fullname: ARRAY OF CHAR);
  366. VAR i, j, len: SIZE;
  367. BEGIN
  368. len := LEN(fullname)-1;
  369. i := 0; WHILE (i < len) & (prefix[i] # 0X) DO fullname[i] := prefix[i]; INC(i) END;
  370. IF (i < len) THEN fullname[i] := ":"; INC(i) END;
  371. j := 0; WHILE (i < len) & (name[j] # 0X) DO fullname[i] := name[j]; INC(i); INC(j) END;
  372. fullname[i] := 0X
  373. END JoinName;
  374. (** Split a pathname at the last PathDelimiter or ":" into path and filename = ( {path (PathDelimiter|":")} filename ) *)
  375. PROCEDURE SplitPath*(pathname: ARRAY OF CHAR; VAR path, name: ARRAY OF CHAR);
  376. VAR i, j, len: SIZE;
  377. BEGIN
  378. i := 0; j := -1;
  379. WHILE pathname[i] # 0X DO
  380. IF (pathname[i] = PathDelimiter) OR (pathname[i] = ":") THEN j := i END;
  381. INC(i)
  382. END;
  383. i := 0; len := LEN(path)-1;
  384. WHILE (i < len) & (i < j) DO path[i] := pathname[i]; INC(i) END; path[i] := 0X;
  385. IF pathname[i] = ":" THEN path[i] := ":"; path[i+1] := 0X; END; (* fof for correct splitting of filenames such as c:test into c: and test*)
  386. INC(j); i := 0; len := LEN(name)-1;
  387. WHILE (i < len) & (pathname[j] # 0X) DO name[i] := pathname[j]; INC(i); INC(j) END;
  388. name[i] := 0X
  389. END SplitPath;
  390. (** Join path and file name = ( path PathDelimiter name ) *)
  391. PROCEDURE JoinPath*(path, name: ARRAY OF CHAR; VAR pathname: ARRAY OF CHAR);
  392. VAR i, j, len: SIZE;
  393. BEGIN
  394. len := LEN(pathname)-1;
  395. i := 0; WHILE (i < len) & (path[i] # 0X) DO pathname[i] := path[i]; INC(i) END;
  396. IF ((i = 0) OR (pathname[i-1] # PathDelimiter)) & (i < len) THEN pathname[i] := PathDelimiter; INC(i) END;
  397. j := 0; WHILE (i < len) & (name[j] # 0X) DO pathname[i] := name[j]; INC(i); INC(j) END;
  398. pathname[i] := 0X
  399. END JoinPath;
  400. (** Split a filename at the last '.' into name and extension = ( name "." extension ) *)
  401. PROCEDURE SplitExtension*(filename: ARRAY OF CHAR; VAR name, extension: ARRAY OF CHAR);
  402. VAR i, j, len: SIZE;
  403. BEGIN
  404. i := 0; j := 0;
  405. WHILE filename[i] # 0X DO
  406. IF filename[i] = "." THEN j := i END;
  407. INC(i)
  408. END;
  409. IF (j = 0) & (filename[0] # ".") THEN (* no extension *)
  410. COPY(filename, name); COPY("", extension)
  411. ELSE
  412. i := 0; len := LEN(name)-1;
  413. WHILE (i < len) & (i < j) DO name[i] := filename[i]; INC(i) END; name[i] := 0X;
  414. INC(j); i := 0; len := LEN(extension)-1;
  415. WHILE (i < len) & (filename[j] # 0X) DO extension[i] := filename[j]; INC(i); INC(j) END;
  416. extension[i] := 0X
  417. END
  418. END SplitExtension;
  419. (** Join name and extension = ( name "." extension ) *)
  420. PROCEDURE JoinExtension*(name, extension: ARRAY OF CHAR; VAR filename: ARRAY OF CHAR);
  421. VAR i,j,len: SIZE;
  422. BEGIN
  423. len := LEN(filename)-1;
  424. i := 0; WHILE (i < len) & (name[i] # 0X) DO filename[i] := name[i]; INC(i) END;
  425. IF ((i = 0) OR (filename[i-1] # ".")) & (i < len) THEN filename[i] := "."; INC(i) END;
  426. j := 0; IF extension[0] = "." THEN INC(j) END;
  427. WHILE (i < len) & (extension[j] # 0X) DO filename[i] := extension[j]; INC(i); INC(j) END;
  428. filename[i] := 0X
  429. END JoinExtension;
  430. (** Append the path delimiter to path if path does not contain one *)
  431. PROCEDURE ForceTrailingDelimiter*(VAR path: ARRAY OF CHAR);
  432. VAR i: SIZE;
  433. BEGIN
  434. i := 0; WHILE path[i] # 0X DO INC(i) END;
  435. IF (i = 0) OR (path[i-1] # PathDelimiter) THEN
  436. path[i] := PathDelimiter;
  437. path[i+1] := 0X
  438. END
  439. END ForceTrailingDelimiter;
  440. (** File system list support. *)
  441. PROCEDURE WriteFS(fs: FileSystem);
  442. BEGIN
  443. IF Trace THEN
  444. IF fs.vol # NIL THEN KernelLog.String(fs.vol.name); KernelLog.Char(" ") END;
  445. KernelLog.String(fs.desc)
  446. END
  447. END WriteFS;
  448. (** Add file system at end of list, with specified prefix, which must be unique. *)
  449. PROCEDURE Add*(fs: FileSystem; prefix: ARRAY OF CHAR);
  450. VAR p, c: FileSystem;
  451. BEGIN {EXCLUSIVE}
  452. IF Trace THEN
  453. KernelLog.Enter; KernelLog.String("Files: Adding "); WriteFS(fs); KernelLog.Exit
  454. END;
  455. COPY(prefix, fs.prefix);
  456. p := NIL; c := fsroot;
  457. WHILE c # NIL DO
  458. ASSERT((c # fs) & (c.prefix # fs.prefix)); (* duplicate insertion not allowed *)
  459. p := c; c := c.next
  460. END;
  461. IF p = NIL THEN fsroot := fs ELSE p.next := fs END;
  462. fs.next := NIL
  463. END Add;
  464. PROCEDURE DeleteFS(fs: FileSystem);
  465. VAR p, c: FileSystem;
  466. BEGIN
  467. p := NIL; c := fsroot;
  468. WHILE c # fs DO p := c; c := c.next END; (* fs must be in list *)
  469. IF p = NIL THEN fsroot := c.next ELSE p.next := c.next END;
  470. c.next := NIL
  471. END DeleteFS;
  472. (** Promote fs to the start of the list. *)
  473. PROCEDURE Promote*(fs: FileSystem);
  474. BEGIN {EXCLUSIVE}
  475. DeleteFS(fs);
  476. fs.next := fsroot; fsroot := fs
  477. END Promote;
  478. (** Remove the file system and finalize it. *)
  479. PROCEDURE Remove*(fs: FileSystem);
  480. VAR
  481. enum: OBJECT
  482. VAR count: LONGINT; fs: FileSystem;
  483. PROCEDURE EnumFile(f: ANY; VAR cont: BOOLEAN);
  484. BEGIN
  485. WITH f: File DO
  486. IF f.fs = fs THEN INC(count); f.Update(); f.fs := NIL END
  487. (* if Update procedure calls back to this module deadlock can result *)
  488. END
  489. END EnumFile;
  490. END;
  491. BEGIN {EXCLUSIVE}
  492. IF Trace THEN
  493. KernelLog.Enter; KernelLog.String("Files: Removing "); WriteFS(fs); KernelLog.Exit
  494. END;
  495. NEW(enum); enum.count := 0; enum.fs := fs;
  496. files.Enumerate(enum.EnumFile);
  497. IF enum.count # 0 THEN
  498. KernelLog.Enter; KernelLog.String("Files: "); KernelLog.Int(enum.count, 1);
  499. KernelLog.String(" open files");
  500. IF fs.vol # NIL THEN
  501. KernelLog.String(" on "); KernelLog.String(fs.vol.name)
  502. END;
  503. KernelLog.Exit
  504. END;
  505. fs.Finalize(); (* potential deadlock *)
  506. DeleteFS(fs)
  507. END Remove;
  508. (* Find the file system with specified prefix. *)
  509. PROCEDURE FindFS(prefix: ARRAY OF CHAR): FileSystem;
  510. VAR fs: FileSystem;
  511. BEGIN
  512. fs := fsroot; WHILE (fs # NIL) & (fs.prefix # prefix) DO fs := fs.next END;
  513. RETURN fs
  514. END FindFS;
  515. (** Find file system with specified prefix. *)
  516. PROCEDURE This*(prefix: ARRAY OF CHAR): FileSystem;
  517. BEGIN {EXCLUSIVE}
  518. RETURN FindFS(prefix)
  519. END This;
  520. (** Get a list of file systems. *)
  521. PROCEDURE GetList*(VAR list: FileSystemTable);
  522. VAR fs: FileSystem; n, i: LONGINT;
  523. BEGIN {EXCLUSIVE}
  524. fs := fsroot; n := 0;
  525. WHILE (fs # NIL) DO fs := fs.next; INC(n) END;
  526. IF n # 0 THEN
  527. NEW(list, n);
  528. fs := fsroot;
  529. FOR i := 0 TO n-1 DO
  530. list[i] := fs; fs := fs.next
  531. END
  532. ELSE
  533. list := NIL
  534. END
  535. END GetList;
  536. (* GC
  537. PROCEDURE Collect(f: ANY);
  538. BEGIN
  539. WITH f: File DO
  540. IF (f.fs # NIL) & (f.fs.vol # NIL) & ~(ReadOnly IN f.fs.vol.flags) THEN
  541. IF ~f.fs.Registered(f) THEN f.fs.Purge(f) END
  542. END
  543. END
  544. END Collect;
  545. *)
  546. (* Find file in open file list, or open and add it. *)
  547. PROCEDURE OpenOld(enum: FileSearcher; fs: FileSystem; VAR fname: ARRAY OF CHAR): File;
  548. VAR f: File; key: LONGINT;
  549. BEGIN
  550. f := NIL;
  551. IF (fs # NIL) & (fname # "") THEN
  552. key := fs.FileKey(fname);
  553. IF key # 0 THEN f := FindOpenFile(enum, fs, key) END;
  554. IF f = NIL THEN (* not found *)
  555. f := fs.Old0(fname);
  556. IF f # NIL THEN
  557. ASSERT(f.key # 0); (* key must be set *)
  558. files.Add(f, NIL);
  559. (* GC
  560. Heaps.RegisterFinalizer(f, Collect); (* to do: use one finalizer for ordering *)
  561. *)
  562. END
  563. END
  564. END;
  565. RETURN f
  566. END OpenOld;
  567. (** Open an existing file, searching through the mounted file system list if no prefix is specified. *)
  568. PROCEDURE Old*(name: ARRAY OF CHAR): File;
  569. VAR fs: FileSystem; f: File; prefix: Prefix; fname: FileName;
  570. BEGIN {EXCLUSIVE}
  571. f := NIL;
  572. SplitName(name, prefix, fname);
  573. IF prefix = "" THEN
  574. fs := fsroot;
  575. WHILE (fs # NIL) & (f = NIL) DO
  576. IF ~(NeedsPrefix IN fs.flags) THEN (* fof *)
  577. f := OpenOld(seacher, fs, fname);
  578. END;
  579. fs := fs.next
  580. END
  581. ELSE
  582. f := OpenOld(seacher, FindFS(prefix), fname)
  583. END;
  584. RETURN f
  585. END Old;
  586. (** Create a new file. If no prefix is specified, create the file on the first file system in the mounted list. *)
  587. PROCEDURE New*(name: ARRAY OF CHAR): File;
  588. VAR fs: FileSystem; f: File; prefix: Prefix; fname: FileName;
  589. BEGIN {EXCLUSIVE}
  590. f := NIL; SplitName(name, prefix, fname);
  591. IF prefix = "" THEN
  592. fs := fsroot; (* use default file system *)
  593. IF fname = "" THEN (* anonymous file on unspecified file system *)
  594. WHILE (fs # NIL) & ((fs.vol = NIL) OR (fs.vol.flags * {Boot,ReadOnly} # {Boot})) DO
  595. fs := fs.next (* find a writable boot file system *)
  596. END;
  597. IF fs = NIL THEN fs := fsroot END (* none found, relapse to default *)
  598. END
  599. ELSE
  600. fs := FindFS(prefix);
  601. END;
  602. IF fs # NIL THEN
  603. IF (fs.vol = NIL) OR ~(ReadOnly IN fs.vol.flags) THEN
  604. f := fs.New0(fname);
  605. (* GC
  606. IF f # NIL THEN
  607. Heaps.RegisterFinalizer(f, Collect)
  608. END
  609. *)
  610. END
  611. END;
  612. RETURN f
  613. END New;
  614. (** Delete a file. res = 0 indicates success. *)
  615. PROCEDURE Delete*(VAR name: ARRAY OF CHAR; VAR res: WORD);
  616. VAR fs: FileSystem; f: File; key: LONGINT; prefix: Prefix; fname: FileName;
  617. BEGIN {EXCLUSIVE}
  618. SplitName(name, prefix, fname);
  619. IF prefix = "" THEN fs := fsroot ELSE fs := FindFS(prefix) END;
  620. IF fs # NIL THEN
  621. IF (fs.vol = NIL) OR ~(ReadOnly IN fs.vol.flags) THEN
  622. fs.Delete0(fname, key, res);
  623. IF key # 0 THEN
  624. LOOP (* remove all occurances of file (fs,key) from collection. *)
  625. f := FindOpenFile(seacher, fs, key);
  626. IF f = NIL THEN EXIT END;
  627. files.Remove(f)
  628. END
  629. END
  630. ELSE
  631. res := VolumeReadOnly (* can not modify read-only volume *)
  632. END
  633. ELSE
  634. res := FsNotFound (* file system not found *)
  635. END
  636. END Delete;
  637. (* copies the file with the given name to the fileClipboard *)
  638. PROCEDURE Copy*(name: ARRAY OF CHAR; VAR res: WORD);
  639. VAR file: File;
  640. BEGIN
  641. res := -1;
  642. file := Old(name);
  643. IF file = NIL THEN RETURN END;
  644. fileClipboard := file;
  645. res := 0;
  646. END Copy;
  647. (* pastes the fileClipboard into the file with the given name if it doesn't exist already *)
  648. PROCEDURE Paste*(name: ARRAY OF CHAR; VAR res: WORD);
  649. VAR writer : Writer;
  650. reader : Reader;
  651. file : File;
  652. chunk : ARRAY 4096 OF CHAR;
  653. len : Streams.BufferOffset;
  654. BEGIN
  655. IF fileClipboard = NIL THEN RETURN END;
  656. IF Old(name) # NIL THEN res := FileAlreadyExists; (* File already exists *)
  657. ELSE
  658. file := New(name);
  659. IF file = NIL THEN res := BadFileName; RETURN END; (* Bad Filename *)
  660. NEW(writer, file, 0);
  661. NEW(reader, fileClipboard, 0);
  662. WHILE (reader.res = Streams.Ok) DO
  663. reader.Bytes(chunk, 0, LEN(chunk), len);
  664. writer.Bytes(chunk, 0, len);
  665. END;
  666. writer.Update;
  667. Register(file);
  668. res := 0;
  669. END;
  670. END Paste;
  671. (**
  672. * Make a copy of a file
  673. * @param source: Prefix, path and name of file to be copied
  674. * @param destination: Prefix, path and name of file to be created
  675. * @param overwrite: @in: Overwrite existing files? @out: Has an existing file been overwritten?
  676. * @param res: @out: Result code
  677. *)
  678. PROCEDURE CopyFile*(source, destination : ARRAY OF CHAR; VAR overwrite : BOOLEAN; VAR res : WORD);
  679. VAR
  680. sprefix, dprefix : Prefix;
  681. sname, dname, dpath, dfilename: FileName;
  682. sPos, dPos : Rider;
  683. sfs, dfs : FileSystem;
  684. sfile, dfile : File;
  685. buffer : ARRAY BufferSize OF CHAR;
  686. i : Streams.Position;
  687. BEGIN
  688. SplitName(source, sprefix, sname);
  689. SplitName(destination, dprefix, dname);
  690. BEGIN {EXCLUSIVE}
  691. IF sprefix = "" THEN sfs := fsroot; ELSE sfs := FindFS(sprefix); END;
  692. IF dprefix = "" THEN dfs := fsroot; ELSE dfs := FindFS(dprefix); END;
  693. IF (sfs # NIL) & (dfs # NIL) THEN (* We found the file system *)
  694. IF (dfs.vol = NIL) OR ~(ReadOnly IN dfs.vol.flags) THEN (* We may write to the target volume *)
  695. sfile := OpenOld(seacher, sfs, sname);
  696. IF sfile # NIL THEN (* We found the source file *)
  697. SplitName(dname, dpath, dfilename);
  698. IF (dfilename # "") THEN
  699. dfile := OpenOld(seacher, dfs, dname);
  700. IF (dfile = NIL) OR overwrite THEN (* We may write to the target file *)
  701. IF dfile # NIL THEN overwrite := TRUE; ELSE overwrite := FALSE; END;
  702. IF overwrite THEN
  703. dfile.GetName(dname);
  704. SplitName(dname, dprefix, dname);
  705. END;
  706. dfile := dfs.New0(dname);
  707. IF dfile # NIL THEN (* We could create the target file *)
  708. res := Ok;
  709. ELSE res := BadFileName;
  710. END;
  711. ELSE res := FileAlreadyExists;
  712. END;
  713. ELSE res := BadFileName;
  714. END;
  715. ELSE res := FileNotFound;
  716. END;
  717. ELSE res :=VolumeReadOnly;
  718. END;
  719. ELSE res := FsNotFound;
  720. END;
  721. END;
  722. IF res # Ok THEN RETURN END;
  723. (* copy file content *)
  724. sfile.Set(sPos, 0); dfile.Set(dPos, 0);
  725. i := 0;
  726. WHILE i < (sfile.Length() DIV BufferSize) DO
  727. sfile.ReadBytes(sPos, buffer, 0, BufferSize);
  728. dfile.WriteBytes(dPos, buffer, 0, BufferSize);
  729. INC(i);
  730. END;
  731. sfile.ReadBytes(sPos, buffer, 0, Streams.BufferOffset(sfile.Length() MOD BufferSize));
  732. dfile.WriteBytes(dPos, buffer, 0, Streams.BufferOffset(sfile.Length() MOD BufferSize));
  733. dfile.Update;
  734. Register(dfile); (* Will enter exclusive region *)
  735. END CopyFile;
  736. (** Rename a file. res = 0 indicates success. *)
  737. PROCEDURE Rename*(CONST old, new: ARRAY OF CHAR; VAR res: WORD);
  738. VAR
  739. key: LONGINT; ofs, nfs: FileSystem; f: File; pold, pnew: Prefix;
  740. fold, fnew: FileName;
  741. BEGIN {EXCLUSIVE}
  742. SplitName(old, pold, fold);
  743. SplitName(new, pnew, fnew);
  744. IF pold = "" THEN ofs := fsroot ELSE ofs := FindFS(pold) END;
  745. IF pnew = "" THEN nfs := fsroot ELSE nfs := FindFS(pnew) END;
  746. IF (nfs # NIL) & (ofs = nfs) THEN
  747. IF (nfs.vol = NIL) OR ~(ReadOnly IN nfs.vol.flags) THEN
  748. key := nfs.FileKey(fold);
  749. IF key # 0 THEN f := FindOpenFile(seacher, nfs, key) ELSE f := NIL END;
  750. nfs.Rename0(fold, fnew, f, res)
  751. ELSE
  752. res := VolumeReadOnly (* can not modify read-only volume *)
  753. END
  754. ELSE
  755. res := FsNotFound (* file system not found *)
  756. END
  757. END Rename;
  758. (** Register a file created with New in the directory, replacing the previous file in the directory with the same name. The file is automatically closed. *)
  759. PROCEDURE Register*(f: File);
  760. VAR res: WORD;
  761. BEGIN {EXCLUSIVE}
  762. IF f # NIL THEN
  763. f.Register0(res);
  764. IF res = 0 THEN (* if register went ok (first time register) *)
  765. ASSERT(f.key # 0);
  766. files.Add(f, NIL)
  767. END
  768. END
  769. END Register;
  770. (** Create a directory structure. Directories are automatically registered. res=0 indicates success.
  771. Use Files.RemoveDirectory to delete a directory *)
  772. PROCEDURE CreateDirectory*(path: ARRAY OF CHAR; VAR res: WORD);
  773. VAR prefix: Prefix; fs: FileSystem; fpath: FileName;
  774. BEGIN {EXCLUSIVE}
  775. SplitName(path, prefix, fpath);
  776. IF prefix = "" THEN fs := fsroot
  777. ELSE fs := FindFS(prefix)
  778. END;
  779. IF fs # NIL THEN fs.CreateDirectory0(fpath, res)
  780. ELSE res := -1
  781. END
  782. END CreateDirectory;
  783. (** Remove a directory. res=0 indicates success. If force=TRUE, any files and subdirectories are automatically deleted. *)
  784. PROCEDURE RemoveDirectory*(path: ARRAY OF CHAR; force: BOOLEAN; VAR res: WORD);
  785. VAR prefix: Prefix; fs: FileSystem; f: File; key: LONGINT; fpath: FileName;
  786. BEGIN {EXCLUSIVE}
  787. SplitName(path, prefix, fpath);
  788. IF prefix = "" THEN fs := fsroot ELSE fs := FindFS(prefix) END;
  789. IF fs # NIL THEN
  790. IF (fs.vol = NIL) OR ~(ReadOnly IN fs.vol.flags) THEN
  791. fs.RemoveDirectory0(fpath, force, key, res);
  792. IF key # 0 THEN
  793. LOOP (* remove all aoccurances of file (fs,key) from collection. *)
  794. f := FindOpenFile(seacher, fs, key);
  795. IF f = NIL THEN EXIT END;
  796. files.Remove(f)
  797. END
  798. END
  799. ELSE
  800. res := VolumeReadOnly (* can not modify read-only volume *)
  801. END
  802. ELSE
  803. res := FsNotFound (* file system not found *)
  804. END
  805. END RemoveDirectory;
  806. (* Enumerates files matching mask *)
  807. PROCEDURE Enumerate(VAR mask: ARRAY OF CHAR; flags: SET; enum: Enumerator);
  808. VAR
  809. fs: FileSystem; ft: FileSystemTable; i: LONGINT;
  810. prefix: Prefix; fmask: FileName;
  811. BEGIN
  812. SplitName(mask, prefix, fmask);
  813. IF prefix = "" THEN
  814. GetList(ft);
  815. IF ft # NIL THEN
  816. (* FIX: deadlock possible if fs containing anonymous file does not allow concurrent Enumerate and Write *)
  817. FOR i := 0 TO LEN(ft^)-1 DO
  818. IF ~(NeedsPrefix IN ft[i].flags) THEN
  819. ft[i].Enumerate0(fmask, flags, enum)
  820. END;
  821. END
  822. END
  823. ELSE
  824. fs := This(prefix);
  825. IF fs # NIL THEN fs.Enumerate0(fmask, flags, enum) END
  826. END
  827. END Enumerate;
  828. PROCEDURE Exists*(CONST fileName: ARRAY OF CHAR; VAR fullName: ARRAY OF CHAR; VAR flags: SET): BOOLEAN;
  829. VAR
  830. fs: FileSystem; ft: FileSystemTable; i: LONGINT;
  831. prefix: Prefix;
  832. BEGIN
  833. IF prefix = "" THEN
  834. GetList(ft);
  835. IF ft # NIL THEN
  836. (* FIX: deadlock possible if fs containing anonymous file does not allow concurrent Enumerate and Write *)
  837. FOR i := 0 TO LEN(ft^)-1 DO
  838. IF ~(NeedsPrefix IN ft[i].flags) THEN
  839. IF ft[i].Has(fileName, fullName, flags) THEN RETURN TRUE END;
  840. END;
  841. END
  842. END
  843. ELSE
  844. fs := This(prefix);
  845. RETURN fs.Has(fileName, fullName, flags);
  846. END;
  847. RETURN FALSE;
  848. END Exists;
  849. (* add a search path to the system *)
  850. PROCEDURE AddSearchPath*(context: Commands.Context);
  851. VAR cmd: ARRAY 32 OF CHAR; msg: ARRAY 256 OF CHAR; res: WORD;
  852. BEGIN
  853. (* preliminary implementation until we know how to solve this generically *)
  854. IF Modules.ModuleByName("HostFiles64") # NIL THEN
  855. cmd := "HostFiles64.AddSearchPath";
  856. END;
  857. IF cmd # "" THEN
  858. Commands.Activate(cmd, context, {Commands.Wait}, res, msg);
  859. IF res # 0 THEN context.error.String(msg); context.error.Ln; END;
  860. END
  861. END AddSearchPath;
  862. (* add a search path to the system *)
  863. PROCEDURE SetWorkPath*(context: Commands.Context);
  864. VAR cmd: ARRAY 32 OF CHAR; msg: ARRAY 256 OF CHAR; res: WORD;
  865. BEGIN
  866. (* preliminary implementation until we know how to solve this generically *)
  867. IF Modules.ModuleByName("HostFiles64") # NIL THEN
  868. cmd := "HostFiles64.SetWorkPath";
  869. END;
  870. IF cmd # "" THEN
  871. Commands.Activate(cmd, context, {Commands.Wait}, res, msg);
  872. IF res # 0 THEN context.error.String(msg); context.error.Ln; END;
  873. END
  874. END SetWorkPath;
  875. (* Find an open file. *)
  876. PROCEDURE FindOpenFile(enum: FileSearcher; fs: FileSystem; key: LONGINT): File;
  877. BEGIN (* not exported, because of possible race condition *)
  878. enum.fs := fs; enum.key := key; enum.found := NIL;
  879. files.Enumerate(enum.EnumFile);
  880. RETURN enum.found
  881. END FindOpenFile;
  882. (** Portable routines to read the standard Oberon types. DEPRECATED, use Streams instead. *)
  883. PROCEDURE ReadSInt*(VAR r: Rider; VAR x: SHORTINT);
  884. BEGIN
  885. r.file.Read(r, SYSTEM.VAL(CHAR, x))
  886. END ReadSInt;
  887. PROCEDURE ReadInt*(VAR r: Rider; VAR x: INTEGER);
  888. BEGIN
  889. r.file.ReadBytes(r, SYSTEM.VAL(Bytes2, x), 0, 2)
  890. END ReadInt;
  891. PROCEDURE ReadLInt*(VAR r: Rider; VAR x: LONGINT);
  892. BEGIN
  893. r.file.ReadBytes(r, SYSTEM.VAL(Bytes4, x), 0, 4)
  894. END ReadLInt;
  895. PROCEDURE ReadHInt*(VAR r: Rider; VAR x: HUGEINT);
  896. BEGIN
  897. r.file.ReadBytes(r, SYSTEM.VAL(Bytes8, x), 0, 8)
  898. END ReadHInt;
  899. PROCEDURE ReadFileSize*(VAR r: Rider; VAR x: Streams.Position);
  900. CONST Size = SIZEOF (Streams.Position);
  901. TYPE Bytes = ARRAY Size OF CHAR;
  902. BEGIN
  903. r.file.ReadBytes(r, SYSTEM.VAL(Bytes, x), 0, Size)
  904. END ReadFileSize;
  905. PROCEDURE ReadSet*(VAR r: Rider; VAR x: SET);
  906. CONST Size = SIZEOF (SET);
  907. TYPE Bytes = ARRAY Size OF CHAR;
  908. BEGIN
  909. r.file.ReadBytes(r, SYSTEM.VAL(Bytes, x), 0, Size)
  910. END ReadSet;
  911. PROCEDURE ReadBool*(VAR r: Rider; VAR x: BOOLEAN);
  912. VAR ch: CHAR;
  913. BEGIN
  914. r.file.Read(r, ch); x := ch # 0X
  915. END ReadBool;
  916. PROCEDURE ReadReal*(VAR r: Rider; VAR x: REAL);
  917. BEGIN
  918. r.file.ReadBytes(r, SYSTEM.VAL(Bytes4, x), 0, 4)
  919. END ReadReal;
  920. PROCEDURE ReadLReal*(VAR r: Rider; VAR x: LONGREAL);
  921. BEGIN
  922. r.file.ReadBytes(r, SYSTEM.VAL(Bytes8, x), 0, 8)
  923. END ReadLReal;
  924. PROCEDURE ReadString*(VAR r: Rider; VAR x: ARRAY OF CHAR);
  925. VAR i: SIZE; ch: CHAR; f: File;
  926. BEGIN
  927. i := 0; f := r.file;
  928. LOOP
  929. f.Read(r, ch); x[i] := ch; INC(i);
  930. IF ch = 0X THEN EXIT END;
  931. IF i = LEN(x) THEN
  932. x[i-1] := 0X;
  933. REPEAT f.Read(r, ch) UNTIL ch = 0X;
  934. EXIT
  935. END
  936. END
  937. END ReadString;
  938. (* Reads a number in compressed format. *)
  939. PROCEDURE ReadNum*(VAR r: Rider; VAR x: LONGINT);
  940. VAR ch: CHAR; n, y: LONGINT; f: File;
  941. BEGIN
  942. n := 0; y := 0; f := r.file;
  943. f.Read(r, ch);
  944. WHILE ch >= 80X DO
  945. INC(y, LSH(LONG(ORD(ch)) - 128, n)); INC(n, 7);
  946. f.Read(r, ch)
  947. END;
  948. x := ASH(LSH(LONG(ORD(ch)), 25), n-25) + y
  949. END ReadNum;
  950. (** Portable routines to write the standard Oberon types. DEPRECATED, used Streams instead. *)
  951. PROCEDURE WriteSInt*(VAR r: Rider; x: SHORTINT);
  952. BEGIN
  953. r.file.Write(r, SYSTEM.VAL(CHAR, x))
  954. END WriteSInt;
  955. PROCEDURE WriteInt*(VAR r: Rider; x: INTEGER);
  956. BEGIN
  957. r.file.WriteBytes(r, SYSTEM.VAL(Bytes2, x), 0, 2)
  958. END WriteInt;
  959. PROCEDURE WriteLInt*(VAR r: Rider; x: LONGINT);
  960. BEGIN
  961. r.file.WriteBytes(r, SYSTEM.VAL(Bytes4, x), 0, 4)
  962. END WriteLInt;
  963. PROCEDURE WriteHInt*(VAR r: Rider; x: HUGEINT);
  964. BEGIN
  965. r.file.WriteBytes(r, SYSTEM.VAL(Bytes8, x), 0, 8)
  966. END WriteHInt;
  967. PROCEDURE WriteFileSize*(VAR r: Rider; x: Streams.Position);
  968. CONST Size = SIZEOF (Streams.Position);
  969. TYPE Bytes = ARRAY Size OF CHAR;
  970. BEGIN
  971. r.file.WriteBytes(r, SYSTEM.VAL(Bytes, x), 0, Size)
  972. END WriteFileSize;
  973. PROCEDURE WriteSet*(VAR r: Rider; x: SET);
  974. CONST Size = SIZEOF (SET);
  975. TYPE Bytes = ARRAY Size OF CHAR;
  976. BEGIN
  977. r.file.WriteBytes(r, SYSTEM.VAL(Bytes, x), 0, Size)
  978. END WriteSet;
  979. PROCEDURE WriteBool*(VAR r: Rider; x: BOOLEAN);
  980. BEGIN
  981. IF x THEN r.file.Write(r, 1X) ELSE r.file.Write(r, 0X) END
  982. END WriteBool;
  983. PROCEDURE WriteReal*(VAR r: Rider; x: REAL);
  984. BEGIN
  985. r.file.WriteBytes(r, SYSTEM.VAL(Bytes4, x), 0, 4)
  986. END WriteReal;
  987. PROCEDURE WriteLReal*(VAR r: Rider; x: LONGREAL);
  988. BEGIN
  989. r.file.WriteBytes(r, SYSTEM.VAL(Bytes8, x), 0, 8)
  990. END WriteLReal;
  991. PROCEDURE WriteString*(VAR r: Rider; x: ARRAY OF CHAR);
  992. VAR i: Streams.BufferOffset;
  993. BEGIN
  994. i := 0; WHILE x[i] # 0X DO INC(i) END;
  995. r.file.WriteBytes(r, x, 0, i+1)
  996. END WriteString;
  997. (* Writes a number in a compressed format. *)
  998. PROCEDURE WriteNum*(VAR r: Rider; x: LONGINT);
  999. VAR f: File;
  1000. BEGIN
  1001. f := r.file;
  1002. WHILE (x < - 64) OR (x > 63) DO
  1003. f.Write(r, CHR(x MOD 128 + 128)); x := x DIV 128
  1004. END;
  1005. f.Write(r, CHR(x MOD 128))
  1006. END WriteNum;
  1007. (** Help procedures. *)
  1008. (** Append first string to second string, truncating on overflow. *)
  1009. PROCEDURE AppendStr*(from: ARRAY OF CHAR; VAR to: ARRAY OF CHAR);
  1010. VAR i, j, m: SIZE;
  1011. BEGIN
  1012. j := 0; WHILE to[j] # 0X DO INC(j) END;
  1013. m := LEN(to)-1;
  1014. i := 0; WHILE (from[i] # 0X) & (j # m) DO to[j] := from[i]; INC(i); INC(j) END;
  1015. to[j] := 0X
  1016. END AppendStr;
  1017. (** Append unsigned integer to string in ASCII format. *)
  1018. PROCEDURE AppendInt*(x: LONGINT; VAR to: ARRAY OF CHAR);
  1019. VAR i: SIZE; m: LONGINT;
  1020. BEGIN
  1021. ASSERT(x >= 0);
  1022. i := 0; WHILE to[i] # 0X DO INC(i) END;
  1023. IF x # 0 THEN
  1024. m := 1000000000;
  1025. WHILE x < m DO m := m DIV 10 END;
  1026. REPEAT
  1027. to[i] := CHR(48 + (x DIV m) MOD 10); INC(i);
  1028. m := m DIV 10
  1029. UNTIL m = 0
  1030. ELSE
  1031. to[i] := "0"; INC(i)
  1032. END;
  1033. to[i] := 0X
  1034. END AppendInt;
  1035. (** Get the dev#part string from the stream *)
  1036. PROCEDURE GetDevPart*(arg : Streams.Reader; VAR deviceName : ARRAY OF CHAR; VAR partition : LONGINT);
  1037. VAR i: SIZE; ch : CHAR;
  1038. BEGIN
  1039. arg.SkipWhitespace;
  1040. deviceName := ""; partition := 0;
  1041. i := 0;
  1042. ch := arg.Peek();
  1043. WHILE (i < LEN(deviceName)-1) & (ch > " ") & (ch # "#") & (ch # ",") & (arg.res = Streams.Ok) DO
  1044. arg.Char(ch); (* consume ch *)
  1045. deviceName[i] := ch; INC(i);
  1046. ch := arg.Peek();
  1047. END;
  1048. deviceName[i] := 0X;
  1049. IF (ch = "#") THEN
  1050. arg.Char(ch); (* consume "#" *)
  1051. arg.Int(partition, FALSE);
  1052. ELSE
  1053. partition := 0;
  1054. END;
  1055. END GetDevPart;
  1056. (* Clean up file systems when shutting down or unloading module. *)
  1057. PROCEDURE FSCleanup;
  1058. VAR ft: FileSystemTable; i: LONGINT;
  1059. BEGIN
  1060. GetList(ft);
  1061. IF ft # NIL THEN
  1062. FOR i := 0 TO LEN(ft^)-1 DO Remove(ft[i]) END
  1063. END
  1064. END FSCleanup;
  1065. (* debugging *)
  1066. (*
  1067. PROCEDURE ShowList*;
  1068. VAR
  1069. enum: OBJECT
  1070. VAR i: LONGINT;
  1071. PROCEDURE EnumFile(f: ANY; VAR cont: BOOLEAN);
  1072. VAR name: FileName;
  1073. BEGIN
  1074. WITH f: File DO
  1075. KernelLog.Int(i, 1); KernelLog.Char(" ");
  1076. (*KernelLog.String(f.fs.prefix); KernelLog.Char(" ");*)
  1077. KernelLog.Address(SYSTEM.VAL(ADDRESS, f)); KernelLog.Char(" ");
  1078. KernelLog.Int(f.key, 1); KernelLog.Char(" ");
  1079. KernelLog.Int(f.Length(), 1); KernelLog.Char(" ");
  1080. f.GetName(name);
  1081. KernelLog.String(name); KernelLog.Ln;
  1082. INC(i)
  1083. END
  1084. END EnumFile;
  1085. END;
  1086. BEGIN
  1087. NEW(enum); enum.i := 0; KernelLog.Ln;
  1088. files.Enumerate(enum.EnumFile)
  1089. END ShowList;
  1090. *)
  1091. BEGIN
  1092. fsroot := NIL; NEW(seacher); NEW(files);
  1093. Modules.InstallTermHandler(FSCleanup)
  1094. END Files64.
  1095. (**
  1096. Notes:
  1097. o A typical code pattern for reading a file is:
  1098. VAR f: Files.File; r: Files.Reader; ch: CHAR;
  1099. f := Files.Old(filename); (* open an existing file *)
  1100. IF f # NIL THEN
  1101. Files.OpenReader(r, f, 0); (* open a buffer on the file *)
  1102. LOOP
  1103. ch := r.Get(); (* read a character from the buffer *)
  1104. IF r.res # Streams.Ok THEN EXIT END; (* end-of-file, or other error *)
  1105. "do something with ch"
  1106. END
  1107. END
  1108. o A typical code pattern for writing a file is:
  1109. VAR f: Files.File; w: Files.Writer; ch: CHAR;
  1110. f := Files.New(filename); (* create a new file (not visible yet) *)
  1111. IF f # NIL THEN
  1112. Files.OpenWriter(w, f, 0); (* open a buffer on the file *)
  1113. WHILE "not done" DO
  1114. "assign ch"
  1115. w.Char(ch) (* write a character to the buffer (if the buffer is full, it is written to the file) *)
  1116. END;
  1117. w.Update; (* write the last buffer to the file *)
  1118. Files.Register(f) (* enter the file in the directory *)
  1119. o See the Streams module for more procedures operating on Reader and Writer buffers, e.g. ReadRawInt, WriteRawInt, etc.
  1120. o Never use an exported identifier with a name ending in "0", unless you are implementing a file system.
  1121. o Never use an exported identifier that is documented as "private".
  1122. o File system implementations must implement the FileKey procedure to assign a unique key value to every file in the file system. The key is used by the Files module to ensure that the Old procedure returns an existing file if it is already open. The key need not be persistent, but must stay unique during a whole session (between mount and unmount). The 0 key is reserved to indicate non-existent files.
  1123. *)
  1124. (*
  1125. On-the-fly GC by bsm
  1126. In order to be non-leaking, a file system must provide the following:
  1127. - FileSystem.Purge -- to reclaim blocks of an open (being closed) file
  1128. - FileSystem.Registered -- reports if a particular open file is registered in the file directory
  1129. The following procedures need to be modified to purge file blocks when appropriate.
  1130. - FileSystem.Register0 -- if an entry to a file, F, which is not open is replaced, purge F.
  1131. - FileSystem.Rename0 -- same as register.
  1132. - FileSystem.Delete0 -- if the entry being deleted refers to a file, F, which is not open, purge F.
  1133. *)
  1134. (*
  1135. Lock order: Files, File, FileSystem
  1136. *)
  1137. Files.File
  1138. Files.File
  1139. Files.Rider
  1140. Files.Reader
  1141. Files.Writer
  1142. Files.Old
  1143. Files.Old
  1144. Files.Set
  1145. Files.OpenReader
  1146. Files.OpenWriter
  1147. Files.ReadNum
  1148. Streams.ReadRawNum
  1149. Files.ReadInt
  1150. Streams.ReadRawInt
  1151. Files.ReadLInt
  1152. Streams.ReadRawLInt
  1153. Files.ReadString
  1154. Streams.ReadRawString
  1155. Files.ReadBytes
  1156. Streams.ReadBytes [add 0 ofs parameter, and len parameter]
  1157. Files.Read(
  1158. Streams.Read(
  1159. Files.ReadBool
  1160. Streams.ReadRawBool
  1161. Files.WriteInt
  1162. Streams.WriteRawInt
  1163. Files.Write(
  1164. Streams.Write(
  1165. Files.WriteBytes
  1166. Streams.WriteBytes [add 0 ofs parameter]