File: OS.m3 Last modified on Fri Mar 10 09:46:19 PST 1995 by kalsow modified on Tue Mar 24 16:04:38 PST 1992 by muller
MODULE---------------------------------------------------------- times ---; IMPORT Atom, AtomList, Env, File, FileRd, FmtTime, FS, Pathname; IMPORT M3File, OSError, Rd, RegularFile, Text, Thread, Time, Wr; IMPORT Default, ErrLog, Text2; PROCEDURE OS IsDirectory (file: TEXT): BOOLEAN = BEGIN TRY WITH stat = FS.Status (file) DO RETURN stat.type = FS.DirectoryFileType; END EXCEPT | OSError.E => RETURN FALSE; END END IsDirectory; PROCEDUREIsExecutable (file: TEXT): BOOLEAN = BEGIN TRY WITH stat = FS.Status (file) DO RETURN stat.type = RegularFile.FileType; END EXCEPT | OSError.E => RETURN FALSE; END END IsExecutable; PROCEDUREFileNameEq (a, b: TEXT): BOOLEAN = BEGIN IF (Default.on_unix) THEN RETURN Text.Equal (a, b); (* POSIX *) ELSE RETURN Text2.CIEqual (a, b); (* WIN32 *) END; END FileNameEq; PROCEDUREFindExecutable (file: TEXT): TEXT = CONST UnixExts = ARRAY OF TEXT { NIL }; CONST WinExts = ARRAY OF TEXT { NIL, "exe", "com", "cmd", "bat" }; VAR path := Env.Get ("PATH"); BEGIN IF Default.on_unix THEN RETURN SearchPath (file, path, ':', UnixExts); ELSE RETURN SearchPath (file, path, ';', WinExts); END; END FindExecutable; PROCEDURESearchPath (file, path: TEXT; sep: CHAR; READONLY exts: ARRAY OF TEXT): TEXT = VAR dir, fn: TEXT; s0, s1, len: INTEGER; no_ext: BOOLEAN; BEGIN IF IsExecutable (file) THEN RETURN file; END; no_ext := Text.Equal (file, Pathname.Base (file)); (* first try the file without looking at the path *) IF no_ext THEN FOR i := FIRST (exts) TO LAST (exts) DO fn := Pathname.Join (NIL, file, exts[i]); IF IsExecutable (fn) THEN RETURN fn; END; END; END; IF path = NIL THEN RETURN NIL; END; IF Pathname.Absolute (file) THEN RETURN NIL; END; (* try the search path *) len := Text.Length (path); s0 := 0; WHILE (s0 < len) DO s1 := Text.FindChar (path, sep, s0); IF (s1 < 0) THEN s1 := len; END; IF (s0 < s1) THEN dir := Text.Sub (path, s0, s1 - s0); IF no_ext THEN FOR i := FIRST (exts) TO LAST (exts) DO fn := Pathname.Join (dir, file, exts[i]); IF IsExecutable (fn) THEN RETURN fn; END; END; ELSE fn := Pathname.Join (dir, file, NIL); IF IsExecutable (fn) THEN RETURN fn; END; END; END; s0 := s1 + 1; END; (* failed *) RETURN NIL; END SearchPath; PROCEDURECopyDirectory (src, dest: TEXT) = VAR nm, src_path, dest_path : TEXT; iter: FS.Iterator; BEGIN TRY IF NOT IsDirectory (dest) THEN FS.CreateDirectory (dest); END; iter := FS.Iterate (src); TRY WHILE iter.next (nm) DO src_path := MakePath (src, nm); dest_path := MakePath (dest, nm); IF IsDirectory (src_path) THEN CopyDirectory (src_path, dest_path); ELSE M3File.Copy (src_path, dest_path); END; END; FINALLY iter.close (); END; EXCEPT OSError.E (ec) => ErrLog.Msg ("trouble copying directory: ", src, " -> ", dest & Err (ec)); END; END CopyDirectory;
VAR epoch: Time.T := Time.Now ();
Internally, we measure time in seconds since we started.
PROCEDURE------------------------------------------------------------------Now (): FileTime = BEGIN RETURN M3ToFileTime (Time.Now ()); END Now; PROCEDUREM3ToFileTime (t: Time.T): FileTime = BEGIN RETURN ROUND (t - epoch); END M3ToFileTime; PROCEDUREFileToM3Time (t: FileTime): Time.T = BEGIN RETURN FLOAT (t, LONGREAL) + epoch; END FileToM3Time; PROCEDURELastModified (file: TEXT): FileTime = BEGIN TRY WITH status = FS.Status (file) DO RETURN M3ToFileTime (status.modificationTime); END EXCEPT | OSError.E => RETURN NO_TIME; END END LastModified; PROCEDUREFmtFileTime (t: FileTime): TEXT = BEGIN RETURN Text2.ConvertNBSP (FmtTime.Short (FileToM3Time (t))); END FmtFileTime;
VAR (* we keep a small cache of allocated file readers... *) spare_mu := NEW (MUTEX); n_spares := 0; spares := ARRAY [0..4] OF FileRd.T { NIL, .. }; PROCEDURE** PROCEDURE DrainFile (f: File.T) = VAR buf: ARRAY [0..1023] OF File.Byte; BEGIN TRY WHILE (f.read (buf) > 0) DO (*skipOpenRd (file: TEXT): Rd.T = VAR rd: FileRd.T := NIL; BEGIN LOCK spare_mu DO IF (n_spares > 0) THEN DEC (n_spares); rd := spares [n_spares]; spares [n_spares] := NIL; END; END; IF (rd = NIL) THEN rd := NEW (FileRd.T); END; TRY EVAL rd.init (FS.OpenFileReadonly (file)); EXCEPT OSError.E (ec) => ErrLog.Msg ("Unable to open \"", file, "\" for reading", Err (ec)); ReleaseRd (rd); RETURN NIL; END; RETURN rd; END OpenRd; PROCEDUREReleaseRd (rd: Rd.T) = BEGIN TYPECASE rd OF | NULL => (* skip *) | FileRd.T (frd) => LOCK spare_mu DO IF (n_spares < NUMBER (spares)) THEN spares [n_spares] := frd; INC (n_spares); END; END; ELSE (* skip *) END; END ReleaseRd; PROCEDURECloseRd (rd: Rd.T) = BEGIN TRY Rd.Close (rd) EXCEPT Rd.Failure, Thread.Alerted => (*SKIP*) END; ReleaseRd (rd); END CloseRd; PROCEDURECloseWr (wr: Wr.T) = BEGIN TRY Wr.Close (wr) EXCEPT Wr.Failure, Thread.Alerted => (*SKIP*) END; END CloseWr; PROCEDURECloseFile (f: File.T) = BEGIN TRY f.close (); EXCEPT OSError.E => (*SKIP*) END; END CloseFile;
END; EXCEPT OSError.E => (* ignore *) END; END DrainFile; ***) PROCEDUREMakePath (a, b, c, d: TEXT := NIL): TEXT = VAR path := a; BEGIN IF (b # NIL) THEN path := Pathname.Join (path, b, NIL); END; IF (c # NIL) THEN path := Pathname.Join (path, c, NIL); END; IF (d # NIL) THEN path := Pathname.Join (path, d, NIL); END; RETURN path; END MakePath; PROCEDUREErr (args: AtomList.T): TEXT = VAR msg : TEXT := NIL; BEGIN WHILE (args # NIL) DO IF (msg = NIL) THEN msg := ": "; ELSE msg := msg & " *** "; END; msg := msg & Atom.ToText (args.head); args := args.tail; END; IF (msg = NIL) THEN msg := ": ** NO INFO **"; END; RETURN msg; END Err; BEGIN END OS.