Oberon/A2/Oberon.ISO9660Files.Mod

(* Aos, Copyright 2001, Pieter Muller, ETH Zurich *) MODULE ISO9660Files;	(** AUTHOR "?/be"; PURPOSE "ISO 9660 File System (ported from Native Oberon)"; *) &#9;IMPORT SYSTEM, Modules, Files, KernelLog, Strings; &#9;CONST &#9;&#9;debug &#61; FALSE; nameDebug &#61; FALSE; &#9;&#9;SS &#61; 2048; &#9;&#9;MaxBufs &#61; 4; &#9;&#9;Directory &#61; 1; &#9;&#9;eFileDoesNotExist &#61; 8903; &#9;&#9;eCannotOpenSubDir &#61; 8916; &#9;&#9;eInvalidFirstCluster &#61; 8917; &#9;&#9;eNameIsWild &#61; 8927; &#9;&#9;eInvalidFileName &#61; 8941; &#9;&#9;eInvalidVolume &#61; 9000; &#9;TYPE &#9;&#9;Filename &#61; ARRAY 256 OF CHAR; &#9;&#9;VolDesc &#61; POINTER TO RECORD &#9;&#9;&#9;root, rootDirSize : LONGINT (* sector number of root directory and root directory size *) &#9;&#9;END; &#9;&#9;Buffer &#61; POINTER TO RECORD (Files.Hint) &#9;&#9;&#9;pos, lim : LONGINT; &#9;&#9;&#9;next : Buffer; &#9;&#9;&#9;data : POINTER TO ARRAY OF CHAR &#9;&#9;END; &#9;&#9;FileSystem &#61; OBJECT(Files.FileSystem) &#9;&#9;&#9;VAR &#9;&#9;&#9;&#9;pri, sup, cur : VolDesc; &#9;&#9;&#9;&#9;jolietLevel : LONGINT; &#9;&#9;&#9;(** Open an existing file. The same file descriptor is returned if a file is opened multiple times. End users use Files.Old instead. *) &#9;&#9;&#9;PROCEDURE Old0(name : ARRAY OF CHAR) : Files.File; &#9;&#9;&#9;VAR f : File; namebuf : Filename; dircl, dirpos, time, date, filecl, len : LONGINT; attr : SET; res : INTEGER; &#9;&#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;&#9;res : &#61; 0; f : &#61; NIL; &#9;&#9;&#9;&#9;Check(name, namebuf, res); &#9;&#9;&#9;&#9;IF res &#61; 0 THEN &#9;&#9;&#9;&#9;&#9;LocateFile(namebuf, SELF, dircl, dirpos, time, date, filecl, len, attr, res); &#9;&#9;&#9;&#9;&#9;IF debug THEN LogString("Old0; filecl : "); LogInt(filecl); LogLn END; &#9;&#9;&#9;&#9;&#9;IF res &#61; 0 THEN &#9;&#9;&#9;&#9;&#9;&#9;IF Directory IN attr THEN res : &#61; eCannotOpenSubDir &#9;&#9;&#9;&#9;&#9;&#9;ELSIF filecl &#60; 16 THEN res : &#61; eInvalidFirstCluster &#9;&#9;&#9;&#9;&#9;&#9;ELSE f : &#61; OpenFile(namebuf, SELF, dircl, dirpos, time, date, filecl, len, attr) &#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;RETURN f &#9;&#9;&#9;END Old0; &#9;&#9;&#9;(** Enumerate canonical file names. mask may contain * wildcards. For internal use only.  End users use Enumerator instead. *) &#9;&#9;&#9;PROCEDURE Enumerate0(mask : ARRAY OF CHAR; flags : SET; enum : Files.Enumerator); &#9;&#9;&#9;VAR &#9;&#9;&#9;&#9;fname, name, mmask, pname, fullname : Filename; &#9;&#9;&#9;&#9;f : Files.File; R : Files.Rider; &#9;&#9;&#9;&#9;attr : SET; bAddToEnum : BOOLEAN; &#9;&#9;&#9;&#9;pos, time, date, len, cl : LONGINT; res : INTEGER; &#9;&#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;&#9;Check(mask, name, res); &#9;&#9;&#9;&#9;IF (res &#61; 0) OR (res &#61; eNameIsWild) THEN &#9;&#9;&#9;&#9;&#9;SeparateName(name, name, mmask); IF (mmask &#61; "") THEN COPY("*", mmask) END; &#9;&#9;&#9;&#9;&#9;Files.JoinName(prefix, name, pname); &#9;&#9;&#9;&#9;&#9;IF nameDebug THEN LogString("Enumerate; dir name : "); LogString(pname); LogLn END; &#9;&#9;&#9;&#9;&#9;f : &#61; OldDir(SELF, name); &#9;&#9;&#9;&#9;&#9;IF f # NIL THEN &#9;&#9;&#9;&#9;&#9;&#9;f.Set(R, 0); pos : &#61; 0; &#9;&#9;&#9;&#9;&#9;&#9;LOOP &#9;&#9;&#9;&#9;&#9;&#9;&#9;MatchFile(R, mmask, fname, pos, cl, time, date, len, attr, res); &#9;&#9;&#9;&#9;&#9;&#9;&#9;IF res # 0 THEN EXIT END; &#9;&#9;&#9;&#9;&#9;&#9;&#9;COPY(pname, fullname); &#9;&#9;&#9;&#9;&#9;&#9;&#9;IF name&#91;0&#93; # 0X THEN Files.AppendStr("/", fullname) END; &#9;&#9;&#9;&#9;&#9;&#9;&#9;Files.AppendStr(fname, fullname); &#9;&#9;&#9;&#9;&#9;&#9;&#9;bAddToEnum : &#61; TRUE; &#9;&#9;&#9;&#9;&#9;&#9;&#9;IF Directory IN attr THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (fname &#61; ".") OR (fname &#61; "..") THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;bAddToEnum : &#61; FALSE; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;flags : &#61; &#123; Files.Directory &#125;; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;len : &#61; 0; &#9;&#9;&#9;&#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;flags : &#61; &#123;&#125;; &#9;&#9;&#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;&#9;&#9;IF bAddToEnum THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;enum.PutEntry(fullname, flags, time, date, len) &#9;&#9;&#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;ELSE res : &#61; eFileDoesNotExist &#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;END &#9;&#9;&#9;END Enumerate0; &#9;&#9;&#9;(** Return the unique non-zero key of the named file, if it exists. *) &#9;&#9;&#9;PROCEDURE FileKey*(name : ARRAY OF CHAR) : LONGINT; &#9;&#9;&#9;VAR res : INTEGER; namebuf : Filename; t, key, filecl : LONGINT; attr : SET; &#9;&#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;&#9;IF nameDebug THEN LogString("OFSFATFiles.FileKey : "); LogString(name); LogLn END; &#9;&#9;&#9;&#9;key : &#61; 0; &#9;&#9;&#9;&#9;Check(name, namebuf, res); &#9;&#9;&#9;&#9;IF res &#61; 0 THEN &#9;&#9;&#9;&#9;&#9;LocateFile(namebuf, SELF, t, t, t, t, filecl, t, attr, res); &#9;&#9;&#9;&#9;&#9;IF res &#61; 0 THEN key : &#61; filecl END &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;RETURN key &#9;&#9;&#9;END FileKey; &#9;&#9;&#9;(** Finalize the file system. *) &#9;&#9;&#9;PROCEDURE Finalize*; &#9;&#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;&#9;vol.Finalize; &#9;&#9;&#9;&#9;Finalize^ &#9;&#9;&#9;END Finalize; &#9;&#9;END FileSystem; &#9;&#9;File &#61; OBJECT (Files.File) &#9;&#9;VAR &#9;&#9;&#9;len, &#9;&#9;&#9;time, date, &#9;&#9;&#9;filecl : LONGINT;	(* first cluster *) &#9;&#9;&#9;attr : SET ;	(* ISO file attributes *) &#9;&#9;&#9;(* directory info *) &#9;&#9;&#9;name : Filename; &#9;&#9;&#9;dircl,	(* start cluster of dir. that contains entry for file *) &#9;&#9;&#9;dirpos : LONGINT;		(* position in cluster of dir. in which entry lies *) &#9;&#9;&#9;nofbufs : INTEGER; &#9;&#9;&#9;firstbuf : Buffer; &#9;&#9;&#9;PROCEDURE Set*(VAR r : Files.Rider; pos : LONGINT); &#9;&#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;&#9;r.eof : &#61; FALSE; r.res : &#61; 0; r.file : &#61; SELF; r.fs : &#61; fs; &#9;&#9;&#9;&#9;IF (pos &#60; 0) THEN r.apos : &#61; 0 &#9;&#9;&#9;&#9;ELSIF (pos &#60; len) THEN r.apos : &#61; pos &#9;&#9;&#9;&#9;ELSE r.apos : &#61; len &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;r.hint : &#61; firstbuf &#9;&#9;&#9;END Set; &#9;&#9;&#9;PROCEDURE Pos*(VAR r : Files.Rider) : LONGINT; &#9;&#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;&#9;RETURN r.apos &#9;&#9;&#9;END Pos; &#9;&#9;&#9;PROCEDURE FindBuf(pos : LONGINT; hint : Buffer) : Buffer; &#9;&#9;&#9;VAR buf : Buffer; &#9;&#9;&#9;BEGIN &#9;&#9;&#9;&#9;buf : &#61; hint; &#9;&#9;&#9;&#9;LOOP &#9;&#9;&#9;&#9;&#9;IF (pos &#62;&#61; buf.pos) &#38; (pos &#60; buf.pos+buf.lim) THEN EXIT END; &#9;&#9;&#9;&#9;&#9;buf : &#61; buf.next; &#9;&#9;&#9;&#9;&#9;IF buf &#61; hint THEN buf : &#61; NIL; EXIT END &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;RETURN buf &#9;&#9;&#9;END FindBuf; &#9;&#9;&#9;PROCEDURE ReadBuf(buf : Buffer; pos : LONGINT); &#9;&#9;&#9;BEGIN &#9;&#9;&#9;&#9;ASSERT(pos &#60;&#61; len, 100); &#9;&#9;&#9;&#9;buf.pos : &#61; pos - pos MOD fs.vol.blockSize; &#9;&#9;&#9;&#9;pos : &#61; pos DIV fs.vol.blockSize; &#9;&#9;&#9;&#9;IF pos &#61; len DIV fs.vol.blockSize THEN buf.lim : &#61; len MOD fs.vol.blockSize &#9;&#9;&#9;&#9;ELSE buf.lim : &#61; fs.vol.blockSize &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;IF debug THEN LogString("ReadBuf; block : "); LogInt(filecl+pos); LogLn END; &#9;&#9;&#9;&#9;fs.vol.GetBlock(filecl+pos, buf.data^) &#9;&#9;&#9;END ReadBuf; &#9;&#9;&#9;PROCEDURE GetBuf(pos : LONGINT; hint : Buffer) : Buffer; &#9;&#9;&#9;VAR buf : Buffer; &#9;&#9;&#9;BEGIN &#9;&#9;&#9;&#9;buf : &#61; FindBuf(pos, hint); &#9;&#9;&#9;&#9;IF buf &#61; NIL THEN &#9;&#9;&#9;&#9;&#9;IF nofbufs &#60; MaxBufs THEN (*allocate new buffer*) &#9;&#9;&#9;&#9;&#9;&#9;NEW(buf); NEW(buf.data, fs.vol.blockSize); &#9;&#9;&#9;&#9;&#9;&#9;buf.next : &#61; firstbuf.next; firstbuf.next : &#61; buf; &#9;&#9;&#9;&#9;&#9;&#9;INC(nofbufs) &#9;&#9;&#9;&#9;&#9;ELSE (*reuse one of the buffers; round robin *) &#9;&#9;&#9;&#9;&#9;&#9;buf : &#61; firstbuf; firstbuf : &#61; buf.next; &#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;ReadBuf(buf, pos); &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;RETURN buf &#9;&#9;&#9;END GetBuf; &#9;&#9;&#9;PROCEDURE Read*(VAR r : Files.Rider; VAR x : CHAR); &#9;&#9;&#9;VAR buf : Buffer; &#9;&#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;&#9;r.res : &#61; 0; &#9;&#9;&#9;&#9;IF (r.apos &#60; len) THEN &#9;&#9;&#9;&#9;&#9;buf : &#61; GetBuf(r.apos, r.hint(Buffer)); &#9;&#9;&#9;&#9;&#9;x : &#61; buf.data&#91;r.apos-buf.pos&#93;; &#9;&#9;&#9;&#9;&#9;INC(r.apos) &#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;x : &#61; 0X; r.eof : &#61; TRUE &#9;&#9;&#9;&#9;END &#9;&#9;&#9;END Read; &#9;&#9;&#9;PROCEDURE ReadBytes*(VAR r : Files.Rider; VAR x : ARRAY OF CHAR; ofs, len : LONGINT); &#9;&#9;&#9;VAR m, pos : LONGINT; src : ADDRESS; buf : Buffer; &#9;&#9;&#9;BEGIN &#123;EXCLUSIVE&#125; &#9;&#9;&#9;&#9;IF LEN(x) &#60; len THEN SYSTEM.HALT(19) END; &#9;&#9;&#9;&#9;IF len &#60;&#61; 0 THEN RETURN END; &#9;&#9;&#9;&#9;buf : &#61; r.hint(Buffer); &#9;&#9;&#9;&#9;m : &#61; SELF.len - r.apos; &#9;&#9;&#9;&#9;IF len &#60;&#61; m THEN r.res : &#61; 0 ELSE r.eof : &#61; TRUE; r.res : &#61; len-m; len : &#61; m END; &#9;&#9;&#9;&#9;WHILE len &#62; 0 DO &#9;&#9;&#9;&#9;&#9;buf : &#61; GetBuf(r.apos, buf); &#9;&#9;&#9;&#9;&#9;pos : &#61; r.apos - buf.pos; &#9;&#9;&#9;&#9;&#9;src : &#61; ADDRESSOF(buf.data&#91;pos&#93;); m : &#61; buf.lim-pos; &#9;&#9;&#9;&#9;&#9;IF m &#62; len THEN m : &#61; len END; &#9;&#9;&#9;&#9;&#9;SYSTEM.MOVE(src, ADDRESSOF(x&#91;ofs&#93;), m); &#9;&#9;&#9;&#9;&#9;DEC(len, m); INC(ofs, m); INC(r.apos, m); &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;r.hint : &#61; buf &#9;&#9;&#9;END ReadBytes; &#9;&#9;&#9;PROCEDURE Length* : LONGINT; &#9;&#9;&#9;BEGIN RETURN len &#9;&#9;&#9;END Length; &#9;&#9;&#9;PROCEDURE GetDate*(VAR t, d : LONGINT); &#9;&#9;&#9;BEGIN t : &#61; time; d : &#61; date &#9;&#9;&#9;END GetDate; &#9;&#9;&#9;PROCEDURE GetName*(VAR name : ARRAY OF CHAR); &#9;&#9;&#9;BEGIN COPY(SELF.name, name) &#9;&#9;&#9;END GetName; &#9;&#9;&#9;PROCEDURE Update*; &#9;&#9;&#9;END Update; (* nothing *) &#9;&#9;END File; &#9;VAR	(* svr *) &#9;&#9;ExtractNameProc : PROCEDURE(VAR dir, name : ARRAY OF CHAR); &#9;(* debug procedures *) &#9;PROCEDURE LogString(s : ARRAY OF CHAR); &#9;BEGIN &#9;&#9;KernelLog.String(s) &#9;END LogString; &#9;PROCEDURE LogInt(i : LONGINT); &#9;BEGIN &#9;&#9;KernelLog.Int(i, 0) &#9;END LogInt; &#9;PROCEDURE LogLn; &#9;BEGIN &#9;&#9;KernelLog.Ln &#9;END LogLn; &#9;(* help procedures *) &#9;(* Get733 - 32 bit, both byte orders *) &#9;PROCEDURE Get733(VAR s : ARRAY OF CHAR; first : LONGINT; VAR d : LONGINT); &#9;BEGIN &#9;&#9;d : &#61; LONG(ORD(s&#91;first&#93;)); &#9;&#9;d : &#61; d + LONG(ORD(s&#91;first+1&#93;))*100H; &#9;&#9;d : &#61; d + LONG(ORD(s&#91;first+2&#93;))*10000H; &#9;&#9;d : &#61; d + LONG(ORD(s&#91;first+3&#93;))*1000000H &#9;END Get733; &#9;(* Check - check filename. Return correct name, or empty name if incorrect. *) &#9;PROCEDURE Check(s : ARRAY OF CHAR; VAR name : Filename; VAR res : INTEGER); &#9;VAR i, j : LONGINT; ch : CHAR; &#9;BEGIN &#9;&#9;IF nameDebug THEN LogString("Check : "); LogString(s) END; &#9;&#9;res : &#61; 0; i : &#61; 0; &#9;&#9;IF (s&#91;0&#93; &#61; "/") OR (s&#91;0&#93; &#61; "\") THEN j : &#61; 1 ELSE j : &#61; 0 END;		(* remove leading / or \ *) &#9;&#9;LOOP &#9;&#9;&#9;ch : &#61; s&#91;j&#93;; &#9;&#9;&#9;IF ch &#61; 0X THEN EXIT END; &#9;&#9;&#9;IF ch &#61; "\" THEN ch : &#61; "/" END; &#9;&#9;&#9;IF (ch &#60; " ") OR (ch &#62;&#61; 07FX) THEN res : &#61; eInvalidFileName; i : &#61; 0; EXIT END; &#9;&#9;&#9;IF (ch &#61; "?") OR (ch &#61; "*") THEN res : &#61; eNameIsWild END; &#9;&#9;&#9;name&#91;i&#93; : &#61; ch; &#9;&#9;&#9;INC(i); INC(j) &#9;&#9;END; &#9;&#9;name&#91;i&#93; : &#61; 0X; &#9;&#9;IF nameDebug THEN LogString(" &#61;&#62; "); LogString(name); LogLn END; &#9;END Check; &#9;PROCEDURE GetVolumeDescriptors(fs : FileSystem; res : INTEGER); &#9;VAR b : ARRAY SS OF CHAR; i : LONGINT; vol : Files.Volume; &#9;BEGIN &#9;&#9;vol : &#61; fs.vol; &#9;&#9;i : &#61; 16; fs.pri : &#61; NIL; fs.sup : &#61; NIL; fs.cur : &#61; NIL; fs.jolietLevel : &#61; 0; &#9;&#9;REPEAT &#9;&#9;&#9;vol.GetBlock(i, b);	(* read boot sector *) &#9;&#9;&#9;CASE b&#91;0&#93; OF &#9;&#9;&#9;&#9;1X : (* Primary volume descriptor *) &#9;&#9;&#9;&#9;&#9;ASSERT(fs.pri &#61; NIL, 102);	(* exactly one primary volume desc *) &#9;&#9;&#9;&#9;&#9;NEW(fs.pri); &#9;&#9;&#9;&#9;&#9;Get733(b, 158, fs.pri.root);	(* location of root directory *) &#9;&#9;&#9;&#9;&#9;Get733(b, 166, fs.pri.rootDirSize)	(* size of root directory in bytes *) &#9;&#9;&#9;&#124;	2X : (* Supplementary volume descriptor *) &#9;&#9;&#9;&#9;&#9;ASSERT(fs.sup &#61; NIL, 103);	(* 0 or 1 supplementary volume descriptor *) &#9;&#9;&#9;&#9;&#9;ASSERT((b&#91;88&#93; &#61; 25X) &#38; (b&#91;89&#93; &#61; 2FX) &#38; ((b&#91;90&#93; &#61; 40X) OR (b&#91;90&#93; &#61; 43X) OR (b&#91;90&#93; &#61; 45X)), 104); &#9;&#9;&#9;&#9;&#9;IF b&#91;90&#93; &#61; 40X THEN fs.jolietLevel : &#61; 1 &#9;&#9;&#9;&#9;&#9;ELSIF b&#91;90&#93; &#61; 43X THEN fs.jolietLevel : &#61; 2 &#9;&#9;&#9;&#9;&#9;ELSIF b&#91;90&#93; &#61; 45X THEN fs.jolietLevel : &#61; 3 &#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;NEW(fs.sup); &#9;&#9;&#9;&#9;&#9;Get733(b, 158, fs.sup.root);	(* location of root directory *) &#9;&#9;&#9;&#9;&#9;Get733(b, 166, fs.sup.rootDirSize)	(* size of root directory in bytes *) &#9;&#9;&#9;ELSE ASSERT((b&#91;0&#93; &#61; 0X) OR (b&#91;0&#93; &#61; 2X) OR (b&#91;0&#93; &#61; 0FFX), 100)	(* boot or end *) &#9;&#9;&#9;END; &#9;&#9;&#9;INC(i) &#9;&#9;UNTIL (res # 0) OR (b&#91;0&#93; &#61; 0FFX); &#9;&#9;IF res &#61; 0 THEN &#9;&#9;&#9;IF fs.pri &#61; NIL THEN res : &#61; eInvalidVolume &#9;&#9;&#9;ELSIF fs.sup # NIL THEN fs.cur : &#61; fs.sup; ExtractNameProc : &#61; ExtractLongName &#9;&#9;&#9;ELSE fs.cur : &#61; fs.pri; ExtractNameProc : &#61; ExtractShortName &#9;&#9;&#9;END &#9;&#9;END; &#9;END GetVolumeDescriptors; &#9;(* GetDir - Get information from a directory entry *) &#9;PROCEDURE GetDir(VAR dir, fname : ARRAY OF CHAR; VAR time, date, cl, len : LONGINT; VAR attr : SET); &#9;VAR t : LONGINT; &#9;BEGIN &#9;&#9;ExtractName(dir, fname); &#9;&#9;t (*attr*) : &#61; ORD(dir&#91;24&#93;); attr : &#61; SYSTEM.VAL(SET, t); &#9;&#9;time : &#61; LONG(ORD(dir&#91;20&#93;))*64*64 + LONG(ORD(dir&#91;21&#93;))*64 + LONG(ORD(dir&#91;22&#93;)); &#9;&#9;date : &#61; LONG(ORD(dir&#91;17&#93;))*32*16 + LONG(ORD(dir&#91;18&#93;))*16 + LONG(ORD(dir&#91;19&#93;)); &#9;&#9;Get733(dir, 1, cl); &#9;&#9;Get733(dir, 9, len) &#9;END GetDir; &#9;PROCEDURE SplitName(str : ARRAY OF CHAR; VAR prefix, name : ARRAY OF CHAR); &#9;VAR i, j : LONGINT; &#9;BEGIN &#9;&#9;IF nameDebug THEN LogString("SplitName : "); LogString(str) END; &#9;&#9;i : &#61; -1; j : &#61; -1; &#9;&#9;REPEAT INC(i); INC(j); prefix&#91;j&#93; : &#61; str&#91;i&#93; UNTIL (str&#91;i&#93; &#61; 0X) OR (str&#91;i&#93; &#61; "/"); &#9;&#9;IF str&#91;i&#93; &#61; "/" THEN &#9;&#9;&#9;prefix&#91;j&#93; : &#61; 0X; j : &#61; -1; &#9;&#9;&#9;REPEAT INC(i); INC(j); name&#91;j&#93; : &#61; str&#91;i&#93; UNTIL name&#91;j&#93; &#61; 0X &#9;&#9;ELSE name&#91;0&#93; : &#61; 0X &#9;&#9;END; &#9;&#9;IF nameDebug THEN LogString(" &#61;&#62; "); LogString(prefix); LogString(", "); LogString(name); LogLn END &#9;END SplitName; &#9;(* SeparateName - separate str into a prefix and a name. *) &#9;PROCEDURE SeparateName(str : ARRAY OF CHAR; VAR prefix : ARRAY OF CHAR; VAR name : Filename); &#9;VAR i, j : LONGINT; &#9;BEGIN &#9;(* Pre : str is result of a Check operation; all "\"s have been changed to "/" *) &#9;&#9;i : &#61; 0; j : &#61; -1; &#9;&#9;WHILE str&#91;i&#93; # 0X DO &#9;&#9;&#9;IF str&#91;i&#93; &#61; "/" THEN j : &#61; i END; &#9;&#9;&#9;INC(i) &#9;&#9;END; &#9;&#9;IF j &#62;&#61; 0 THEN &#9;&#9;&#9;COPY(str, prefix); prefix&#91;j&#93; : &#61; 0X; &#9;&#9;&#9;i : &#61; -1; &#9;&#9;&#9;REPEAT INC(i); INC(j); name&#91;i&#93; : &#61; str&#91;j&#93; UNTIL name&#91;i&#93; &#61; 0X &#9;&#9;ELSE COPY(str, name); prefix&#91;0&#93; : &#61; 0X &#9;&#9;END &#9;END SeparateName; &#9;PROCEDURE ExtractShortName(VAR dir, name : ARRAY OF CHAR); &#9;VAR i, j, len : LONGINT; &#9;BEGIN &#9;&#9;len : &#61; ORD(dir&#91;31&#93;); &#9;&#9;i : &#61; 0; j : &#61; 32; &#9;&#9;WHILE (i &#60; len) &#38; (dir&#91;j&#93; # ";") DO name&#91;i&#93; : &#61; dir&#91;j&#93;; INC(i); INC(j) END; &#9;&#9;name&#91;i&#93; : &#61; 0X; &#9;END ExtractShortName; &#9;PROCEDURE ExtractLongName(VAR dir, name : ARRAY OF CHAR); &#9;VAR i, j, end : LONGINT; &#9;BEGIN &#9;&#9;end : &#61; 33+ORD(dir&#91;31&#93;); &#9;&#9;i : &#61; 0; j : &#61; 33; &#9;&#9;WHILE (j &#60; end) &#38; (dir&#91;j&#93; # ";") DO name&#91;i&#93; : &#61; dir&#91;j&#93;; INC(i); INC(j, 2) END; &#9;&#9;name&#91;i&#93; : &#61; 0X; &#9;END ExtractLongName; &#9;PROCEDURE ExtractName(VAR dir, name : ARRAY OF CHAR); &#9;VAR len : LONGINT; &#9;BEGIN &#9;&#9;len : &#61; ORD(dir&#91;31&#93;); &#9;&#9;IF len &#61; 1 THEN &#9;&#9;&#9;IF dir&#91;33&#93; &#61; 0X THEN COPY(".", name) &#9;&#9;&#9;ELSIF dir&#91;33&#93; &#61; 1X THEN COPY("..", name) &#9;&#9;&#9;END &#9;&#9;ELSE ExtractNameProc(dir, name) &#9;&#9;END &#9;END ExtractName; &#9;PROCEDURE MatchFile(VAR R : Files.Rider; name : ARRAY OF CHAR; VAR fname : ARRAY OF CHAR; &#9;&#9;VAR pos, cl, time, date, len : LONGINT; VAR attr : SET; VAR res : INTEGER); &#9;VAR &#9;&#9;found : BOOLEAN; f : File; fs : FileSystem; buf : ARRAY 256 OF CHAR; entryLen, padding : LONGINT; &#9;BEGIN &#9;&#9;f : &#61; R.file(File); fs : &#61; R.fs(FileSystem); &#9;&#9;found : &#61; FALSE; &#9;&#9;LOOP &#9;&#9;&#9;pos : &#61; R.file.Pos(R); &#9;&#9;&#9;IF debug THEN LogString("MatchFile; pos : "); LogInt(pos); LogLn END; &#9;&#9;&#9;R.file.Read(R, buf&#91;0&#93;); entryLen : &#61; ORD(buf&#91;0&#93;); &#9;&#9;&#9;IF debug THEN LogString("MatchFile; entryLen : "); LogInt(entryLen); LogLn END; &#9;&#9;&#9;IF R.eof THEN &#9;&#9;&#9;&#9;IF debug THEN LogString("MatchFile; eof"); LogLn END; &#9;&#9;&#9;&#9;EXIT &#9;&#9;&#9;END; &#9;&#9;&#9;IF entryLen &#62; 0 THEN &#9;&#9;&#9;&#9;R.file.ReadBytes(R, buf, 0, entryLen-1); &#9;&#9;&#9;&#9;GetDir(buf, fname, time, date, cl, len, attr); &#9;&#9;&#9;&#9;found : &#61; Strings.Match(name, fname); &#9;&#9;&#9;ELSE (* Directory entries may not cross sectors. Skip padding bytes *) &#9;&#9;&#9;&#9;padding : &#61; SS - (R.file.Pos(R) MOD SS); &#9;&#9;&#9;&#9;IF (padding &#62; 0) &#38; (padding &#60;&#61; 256) THEN &#9;&#9;&#9;&#9;&#9;IF debug THEN LogString("MatchFile; skip padding : "); LogInt(padding); LogLn; END; &#9;&#9;&#9;&#9;&#9;R.file.ReadBytes(R, buf, 0, padding); (* Skip padding *) &#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;IF debug THEN LogString("MatchFile; Confusing directory structure... givin&#39; up"); END; &#9;&#9;&#9;&#9;&#9;EXIT; &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;END; &#9;&#9;&#9;IF found THEN EXIT END; &#9;&#9;END; &#9;&#9;IF found THEN res : &#61; 0 ELSE res : &#61; eInvalidFileName END; &#9;END MatchFile; &#9;PROCEDURE FindFile(fs : FileSystem; name : ARRAY OF CHAR; dircl, dirlen : LONGINT; &#9;&#9;VAR dirpos, time, date, filecl, len : LONGINT; VAR attr : SET; VAR res : INTEGER); &#9;VAR f : File; R : Files.Rider; fname : Filename; &#9;BEGIN &#9;&#9;ASSERT(name # "", 100); &#9;&#9;f : &#61; OpenFile("", fs, -1, -1, -1, -1, dircl, dirlen, &#123;&#125;); &#9;&#9;f.Set(R, 0); &#9;&#9;MatchFile(R, name, fname, dirpos, filecl, time, date, len, attr, res); &#9;END FindFile; &#9;PROCEDURE LocateFile(name : ARRAY OF CHAR; fs : FileSystem; &#9;&#9;VAR dircl, dirpos, time, date, filecl, len : LONGINT; VAR attr : SET; VAR res : INTEGER); &#9;VAR cur : Filename; dirlen : LONGINT; &#9;BEGIN &#9;&#9;res : &#61; 0; &#9;&#9;dircl : &#61; fs.cur.root; dirlen : &#61; fs.cur.rootDirSize;	(* start in root directory *) &#9;&#9;IF name&#91;0&#93; &#61; 0X THEN (* root dir *) &#9;&#9;&#9;filecl : &#61; dircl; attr : &#61; &#123;Directory&#125;; &#9;&#9;&#9;len : &#61; dirlen; dirpos : &#61; -1; &#9;&#9;ELSE &#9;&#9;&#9;LOOP &#9;&#9;&#9;&#9;SplitName(name, cur, name); &#9;&#9;&#9;&#9;FindFile(fs, cur, dircl, dirlen, dirpos, time, date, filecl, len, attr, res); &#9;&#9;&#9;&#9;IF (res &#61; 0) &#38; (name # "") &#38; &#126;(Directory IN attr) THEN res : &#61; eInvalidFileName END; &#9;&#9;&#9;&#9;IF (res # 0) OR (name &#61; "") THEN EXIT END; &#9;&#9;&#9;&#9;dircl : &#61; filecl; dirlen : &#61; len &#9;&#9;&#9;END &#9;&#9;END &#9;END LocateFile; &#9;PROCEDURE OpenFile(name : ARRAY OF CHAR; fs : FileSystem; dircl, dirpos, time, date, filecl, len : LONGINT; attr : SET) : File; &#9;VAR f : File; buf : Buffer; &#9;BEGIN &#9;&#9;NEW(f); COPY(name, f.name); f.fs : &#61; fs; f.key : &#61; filecl; &#9;&#9;f.dircl : &#61; dircl; f.dirpos : &#61; dirpos; f.time : &#61; time; f.date : &#61; date; &#9;&#9;f.filecl : &#61; filecl; f.len : &#61; len; f.attr : &#61; attr; &#9;&#9;NEW(buf); buf.next : &#61; buf; &#9;&#9;NEW(buf.data, fs.vol.blockSize); &#9;&#9;IF f.len &#61; 0 THEN buf.pos : &#61; 0; buf.lim : &#61; 0 &#9;&#9;ELSE f.ReadBuf(buf, 0) (* file is not empty *) &#9;&#9;END; &#9;&#9;f.firstbuf : &#61; buf; f.nofbufs : &#61; 1; &#9;&#9;RETURN f &#9;END OpenFile; &#9;PROCEDURE OldDir(fs : Files.FileSystem; name : ARRAY OF CHAR) : Files.File; &#9;VAR f : File; dircl, dirpos, time, date, filecl, len : LONGINT; attr : SET; res : INTEGER; &#9;BEGIN &#9;&#9;res : &#61; 0; f : &#61; NIL; &#9;&#9;LocateFile(name, fs(FileSystem), dircl, dirpos, time, date, filecl, len, attr, res); &#9;&#9;IF res &#61; 0 THEN &#9;&#9;&#9;IF &#126;(Directory IN attr) THEN res : &#61; eCannotOpenSubDir &#9;&#9;&#9;ELSIF filecl &#60; 16 THEN res : &#61; eInvalidFirstCluster &#9;&#9;&#9;ELSE f : &#61; OpenFile(name, fs(FileSystem), dircl, dirpos, time, date, filecl, len, attr) &#9;&#9;&#9;END &#9;&#9;END; &#9;&#9;RETURN f &#9;END OldDir; (** Generate a new file system object. OFS.NewVol has volume parameter, OFS.Par has mount prefix. *) PROCEDURE NewFS*(context : Files.Parameters); VAR fs : FileSystem; res : INTEGER; BEGIN &#9;IF Files.This(context.prefix) &#61; NIL THEN &#9;&#9;NEW(fs); fs.vol : &#61; context.vol; &#9;&#9;GetVolumeDescriptors(fs, res); &#9;&#9;IF res &#61; 0 THEN &#9;&#9;&#9;IF debug THEN &#9;&#9;&#9;&#9;LogString(" primary root : "); LogInt(fs.pri.root); LogLn; &#9;&#9;&#9;&#9;LogString(" primary root dir size : "); LogInt(fs.pri.rootDirSize); LogLn &#9;&#9;&#9;END; &#9;&#9;&#9;fs.desc&#91;0&#93; : &#61; 0X; Files.AppendStr(context.vol.name, fs.desc);  Files.AppendStr(" / ISO9660FS", fs.desc); &#9;&#9;&#9;Files.Add(fs, context.prefix) &#9;&#9;ELSE context.error.String("No ISO9660 file system found on "); context.error.String(context.vol.name); context.error.Ln; &#9;&#9;END &#9;ELSE context.error.String(context.prefix); context.error.String(" already in use"); context.error.Ln; &#9;END; END NewFS; (* Clean up when module freed. *) PROCEDURE Cleanup; VAR ft : Files.FileSystemTable; i : LONGINT; BEGIN &#9;IF Modules.shutdown &#61; Modules.None THEN &#9;&#9;Files.GetList(ft); &#9;&#9;IF ft # NIL THEN &#9;&#9;&#9;FOR i : &#61; 0 TO LEN(ft^)-1 DO &#9;&#9;&#9;&#9;IF ft&#91;i&#93; IS FileSystem THEN Files.Remove(ft&#91;i&#93;) END &#9;&#9;&#9;END &#9;&#9;END &#9;END END Cleanup; BEGIN &#9;Modules.InstallTermHandler(Cleanup) END ISO9660Files. ISO9660Volumes.Mod Partitions.Show OFSTools.Mount CD IsoFS IDE2 &#126; OFSTools.Unmount CD0 &#126; SystemTools.Free ISO9660Files ISO9660Volumes &#126; System.Directory CD : *.* &#126; History : 30.05.2006	Fixed MatchFile : Directory entries may not cross sector boundaries. Skip padding bytes if necessary. (staubesv)