123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215 |
- (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
- MODULE UnixFiles; (** AUTHOR "gf"; PURPOSE "Unix file systems" *)
- (* derived fron (SPARCOberon) Files.Mod by J. Templ 1.12. 89/14.05.93 *)
- IMPORT S := SYSTEM, Unix, Machine, Heaps, Objects, Kernel, Modules, Log := KernelLog, Files;
- CONST
- NBufs = 4; Bufsize = 4096; (*FileTabSize = 1024; ResFiles = 128; *) NoDesc = -1;
- Open = 0; Create = 1; Closed = 2; (* file states *)
-
- NoKey = -1;
-
- TraceCollection = 0;
- TraceSearch=1;
- Trace = {};
- VAR
- tempno: INTEGER;
- openfiles: INTEGER;
-
- searchPath: ARRAY 1024 OF CHAR;
- cwd: ARRAY 256 OF CHAR;
-
- unixFS: UnixFileSystem; (* must be unique *)
- collection: Collection; (* must be unique *)
- TYPE
- Filename = ARRAY 256 OF CHAR;
-
- NameSet = OBJECT
- VAR
- name: ARRAY 64 OF CHAR;
- left, right: NameSet;
- PROCEDURE Add( CONST filename: ARRAY OF CHAR ): BOOLEAN;
- (* add filename if it not already exists. else return false *)
- BEGIN
- IF filename = name THEN RETURN FALSE END;
- IF filename < name THEN
- IF left = NIL THEN NEW( left, filename ); RETURN TRUE
- ELSE RETURN left.Add( filename )
- END
- ELSE
- IF right = NIL THEN NEW( right, filename ); RETURN TRUE
- ELSE RETURN right.Add( filename )
- END
- END
- END Add;
- PROCEDURE & Init( CONST filename: ARRAY OF CHAR );
- BEGIN
- COPY( filename, name );
- left := NIL; right := NIL
- END Init;
- END NameSet;
-
- AliasFileSystem = OBJECT (Files.FileSystem)
- VAR
- fs: UnixFileSystem;
-
- PROCEDURE & Init*( realFS: UnixFileSystem);
- BEGIN
- SELF.fs := realFS;
- END Init;
- PROCEDURE New0( name: ARRAY OF CHAR ): Files.File;
- VAR f: Files.File;
- BEGIN
- f := fs.New0( name );
- IF f # NIL THEN f.fs := SELF END;
- RETURN f;
- END New0;
- PROCEDURE Old0( name: ARRAY OF CHAR ): Files.File;
- VAR f: Files.File;
- BEGIN
- f := fs.Old0( name );
- IF f # NIL THEN f.fs := SELF END;
- RETURN f;
- END Old0;
- PROCEDURE Delete0( name: ARRAY OF CHAR; VAR key, res: LONGINT );
- BEGIN
- fs.Delete0( name, key, res );
- END Delete0;
- PROCEDURE Rename0( old, new: ARRAY OF CHAR; fold: Files.File; VAR res: LONGINT );
- BEGIN
- fs.Rename0( old, new, fold, res );
- END Rename0;
- PROCEDURE Enumerate0( mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator );
- BEGIN
- fs.Enumerate0( mask, flags, enum );
- END Enumerate0;
- PROCEDURE FileKey( name: ARRAY OF CHAR ): LONGINT;
- VAR
- BEGIN
- RETURN fs.FileKey( name );
- END FileKey;
- PROCEDURE CreateDirectory0( name: ARRAY OF CHAR; VAR res: LONGINT );
- BEGIN
- fs.CreateDirectory0( name, res );
- END CreateDirectory0;
- PROCEDURE RemoveDirectory0( name: ARRAY OF CHAR; force: BOOLEAN; VAR key, res: LONGINT );
- BEGIN
- fs.RemoveDirectory0( name, force, key, res );
- END RemoveDirectory0;
- END AliasFileSystem;
-
-
- FinalizeFiles = OBJECT
- PROCEDURE EnumFile( f: ANY; VAR cont: BOOLEAN );
- VAR F: File;
- BEGIN
- F := f( File ); F.Finalize(); cont := TRUE
- END EnumFile;
- END FinalizeFiles;
-
-
- SearchByName = OBJECT
- VAR sname: Filename;
- found: File;
- PROCEDURE Init( name: ARRAY OF CHAR );
- BEGIN
- found := NIL; COPY(name, sname); (* UpperCase( name, sname )*)
- END Init;
- PROCEDURE EnumFile( f: ANY; VAR cont: BOOLEAN );
- VAR F: File; fname: Filename;
- BEGIN
- F := f( File ); (* UpperCase( F.workName, fname );*)
- IF TraceSearch IN Trace THEN Log.String( "Enumerate: " ); Log.String( fname );
- END;
- IF sname = fname THEN found := F; cont := FALSE ELSE cont := TRUE END;
- IF TraceSearch IN Trace THEN
- IF cont THEN Log.String( " # " ); ELSE Log.String( " = " ); END;
- Log.String( sname ); Log.Ln;
- END;
- END EnumFile;
- END SearchByName;
-
- SearchByFstat = OBJECT
- VAR
- found: File;
- stat: Unix.Status;
-
- PROCEDURE Init( s: Unix.Status );
- BEGIN
- found := NIL;
- stat := s;
- END Init;
- PROCEDURE EnumFile( f: ANY; VAR cont: BOOLEAN );
- VAR F: File; fname: Filename;
- BEGIN
- WITH f: File DO
- IF (stat.ino = f.ino) & (stat.dev = f.dev) THEN
- (* possible different name but same file! *)
- ResetBuffers( f, stat );
- found := f; cont := FALSE;
- END;
- END;
- END EnumFile;
- END SearchByFstat;
-
-
- Collection = OBJECT (* methods in Collection shared by objects Filesystem and File *)
- VAR oldFiles, newFiles: Kernel.FinalizedCollection;
- search: SearchByName;
- ssearch: SearchByFstat;
- fileKey: LONGINT;
- PROCEDURE & Init*;
- BEGIN
- NEW( oldFiles ); NEW( newFiles ); NEW( search ); NEW(ssearch); fileKey := -1;
- END Init;
- PROCEDURE GetNextFileKey( ): LONGINT;
- BEGIN {EXCLUSIVE}
- DEC( fileKey ); RETURN fileKey
- END GetNextFileKey;
- PROCEDURE Register( F: File );
- BEGIN {EXCLUSIVE}
- IF TraceCollection IN Trace THEN Log.String( "Collections.Register " ); Log.String( F.workName ); Log.Ln; END;
- oldFiles.Add( F, FinalizeFile ); newFiles.Remove( F ); DEC( fileKey ); (*F.Init( F.workName, F.hfile, fileKey,F.fileSystem );*)
- END Register;
- PROCEDURE Unregister( F: File );
- BEGIN {EXCLUSIVE}
- IF TraceCollection IN Trace THEN Log.String( "Unregister " ); Log.String( F.workName ); Log.Ln; END;
- oldFiles.Remove( F ); newFiles.Add( F, FinalizeFile (* FinalizeFile*) ); (*F.Init( F.workName, Kernel32.InvalidHandleValue, 0, F.fileSystem );*)
- END Unregister;
- PROCEDURE AddNew( F: File );
- BEGIN {EXCLUSIVE}
- IF TraceCollection IN Trace THEN Log.String( "Collections.AddNew: " ); Log.String( F.workName ); Log.Ln; END;
- newFiles.Add( F, FinalizeFile );
- END AddNew;
- PROCEDURE AddOld( F: File );
- BEGIN {EXCLUSIVE}
- IF TraceCollection IN Trace THEN Log.String( "Collections.AddOld: " ); Log.String( F.workName ); Log.Ln; END;
- oldFiles.Add( F, FinalizeFile );
- END AddOld;
- PROCEDURE ByStat(CONST stat: Unix.Status): File;
- BEGIN{EXCLUSIVE}
- ssearch.Init(stat);
- oldFiles.Enumerate(ssearch.EnumFile);
- IF ssearch.found = NIL THEN
- newFiles.Enumerate(ssearch.EnumFile)
- END;
-
- IF TraceCollection IN Trace THEN
- Log.String( "Collections.ByStatus: " ); Log.Ln;
- IF ssearch.found = NIL THEN Log.String("not found") ELSE Log.String("found") END;
- END;
-
- RETURN ssearch.found;
- END ByStat;
-
- PROCEDURE ByName( VAR fname: ARRAY OF CHAR ): File;
- BEGIN {EXCLUSIVE}
- IF TraceCollection IN Trace THEN Log.String( "Collections.ByName: " ); Log.String( fname ); Log.Ln; END;
- search.Init( fname ); oldFiles.Enumerate( search.EnumFile ); RETURN search.found
- END ByName;
- PROCEDURE ByNameNotGC( VAR fname: ARRAY OF CHAR ): File;
- BEGIN {EXCLUSIVE}
- IF TraceCollection IN Trace THEN Log.String( "Collections.ByName: " ); Log.String( fname ); Log.Ln; END;
- search.Init( fname ); oldFiles.EnumerateN( search.EnumFile ); RETURN search.found;
- END ByNameNotGC;
- PROCEDURE Finalize;
- VAR fin: FinalizeFiles;
- BEGIN {EXCLUSIVE}
- IF TraceCollection IN Trace THEN Log.String( "Collections.Finalize " ); Log.Ln; END;
- NEW( fin ); newFiles.Enumerate( fin.EnumFile ); newFiles.Clear(); oldFiles.Enumerate( fin.EnumFile ); oldFiles.Clear();
- END Finalize;
- PROCEDURE FinalizeFile( obj: ANY );
- VAR F: File;
- BEGIN
- F := obj( File );
- IF TraceCollection IN Trace THEN Log.String( "Collections.FinalizeFile " ); Log.String( F.workName ); Log.Ln; END;
- F.Finalize()
- END FinalizeFile;
- END Collection;
- UnixFileSystem* = OBJECT (Files.FileSystem)
- PROCEDURE & Init;
- BEGIN
- prefix := ""; vol := NIL; desc := "UnixFS"
- END Init;
- PROCEDURE New0*( name: ARRAY OF CHAR ): Files.File;
- VAR f: File;
- BEGIN {EXCLUSIVE}
- (*AwaitFinalizingDone;*)
- NEW( f, SELF );
- f.workName := ""; COPY( name, f.registerName );
- f.fd := NoDesc; f.state := Create; f.fsize := 0; f.fpos := 0;
- f.swapper := -1; (*all f.buf[i] = NIL*)
- f.key := NoKey; f.fs := SELF;
- RETURN f
- END New0;
-
-
- PROCEDURE IsDirectory( VAR stat: Unix.Status ): BOOLEAN;
- VAR mode: LONGINT;
- BEGIN
- mode := stat.mode;
- RETURN ODD( mode DIV 4000H )
- END IsDirectory;
- PROCEDURE Old0*( name: ARRAY OF CHAR ): Files.File;
- VAR f: File; stat: Unix.Status; fd, r, errno, pos: LONGINT;
- oflags: SET; nextdir, path: Filename;
- BEGIN {EXCLUSIVE}
- IF name = "" THEN RETURN NIL END;
-
- IF IsFullName( name ) THEN
- COPY( name, path ); nextdir := "";
- ELSE
- pos := 0; ScanPath( pos, nextdir ); MakePath( nextdir, name, path );
- ScanPath( pos, nextdir )
- END;
-
- (*IF (FileTabSize - openfiles) < ResFiles THEN (*! GC *) END;*)
-
- LOOP
- r := Unix.access( ADDRESSOF( path ), Unix.R_OK );
- IF r >= 0 THEN
- r := Unix.access( ADDRESSOF( path ), Unix.W_OK );
- IF r < 0 THEN oflags := Unix.rdonly ELSE oflags := Unix.rdwr END;
-
- fd := Unix.open( ADDRESSOF( path ), oflags, {} ); errno := Unix.errno();
- IF ((fd < 0) & (errno IN {Unix.ENFILE, Unix.EMFILE})) (* OR (fd >= FileTabSize)*) THEN
- (*IF fd > 0 THEN r := Unix.p( fd ) END;*)
- (*!GC ;*)
- fd := Unix.open( ADDRESSOF( path ), oflags, {} ); errno := Unix.errno();
- END;
- IF fd >= 0 THEN
- r := Unix.fstat( fd, stat );
- f := collection.ByStat(stat);
- (*f := FindCachedEntry( stat );*)
- IF f # NIL THEN
- (* use the file already cached *) r := Unix.close( fd );
- EXIT
- ELSE
- (*ELSIF fd < FileTabSize THEN*)
- (*AwaitFinalizingDone;*)
- NEW( f, SELF );
- f.fd := fd; f.dev := stat.dev; f.ino := stat.ino;
- f.mtime := stat.mtime.sec; f.fsize := stat.size; f.fpos := 0;
- f.state := Open; f.swapper := -1; (*all f.buf[i] = NIL*)
- COPY( path, f.workName ); f.registerName := "";
- f.tempFile := FALSE;
- IF IsDirectory( stat ) THEN
- f.flags := {Files.Directory, Files.ReadOnly}
- ELSIF oflags = Unix.rdonly THEN
- f.flags := {Files.ReadOnly}
- END;
- f.key := NoKey; f.fs := SELF;
- (*fileTab[fd].f := f; (* cache file *)*)
- INC( openfiles );
- collection.AddOld(f);
- (*RegisterFinalizer( f, Cleanup );*)
- EXIT
- (*ELSE
- r := Unix.close( fd );
- Halt( f, FALSE, "UnixFiles.File.Old0: too many files open" );
- *)
- END
- END
- ELSIF nextdir # "" THEN
- MakePath( nextdir, name, path ); ScanPath( pos, nextdir );
- ELSE
- f := NIL; EXIT
- END;
- END; (* loop *)
- RETURN f
- END Old0;
- (** Return the unique non-zero key of the named file, if it exists. *)
- PROCEDURE FileKey*( name: ARRAY OF CHAR ): LONGINT;
- (* Can not be used for Unix files as LONGINT is too small.
- In the Unix filesystem a file is identified by
- - dev (64 bit (Linux), 32 bit (Solaris, Darwin)) +
- - ino (32 bit)
- *)
- BEGIN
- RETURN 0
- END FileKey;
- PROCEDURE Delete0*( name: ARRAY OF CHAR; VAR key, res: LONGINT );
- VAR r: LONGINT;
- BEGIN {EXCLUSIVE}
- r := Unix.unlink( ADDRESSOF( name ) );
- IF r = 0 THEN res := Files.Ok
- ELSE res := Unix.errno( )
- END;
- key := 0;
- END Delete0;
- PROCEDURE Rename0*( old, new: ARRAY OF CHAR; f: Files.File; VAR res: LONGINT );
- CONST Bufsize = 4096;
- VAR fdold, fdnew, n, r: LONGINT; ostat, nstat: Unix.Status;
- buf: ARRAY Bufsize OF CHAR;
- BEGIN {EXCLUSIVE}
- r:= Unix.stat( ADDRESSOF( old ), ostat );
- IF r >= 0 THEN
- r := Unix.stat( ADDRESSOF( new ), nstat );
- IF (r >= 0) & ((ostat.dev # nstat.dev) OR (ostat.ino # nstat.ino)) THEN
- r := Unix.unlink( ADDRESSOF( new ) ) (* work around stale nfs handles *);
- END;
- r := Unix.rename( ADDRESSOF( old ), ADDRESSOF( new ) );
- IF r < 0 THEN
- res := Unix.errno( );
- IF (res = Unix.EXDEV) OR (res = Unix.ETXTBSY) THEN (* cross device link, move the file / file busy frequently happens in VirtualBox *)
- fdold := Unix.open( ADDRESSOF( old ), Unix.rdonly, {} );
- IF fdold < 0 THEN
- res := Unix.errno( );
- RETURN
- END;
- fdnew := Unix.open( ADDRESSOF( new ), Unix.rdwr + Unix.creat + Unix.trunc, Unix.rwrwr );
- IF fdnew < 0 THEN
- res := Unix.errno( );
- RETURN
- END;
- n := Unix.read( fdold, ADDRESSOF( buf ), Bufsize );
- WHILE n > 0 DO
- r := Unix.write( fdnew, ADDRESSOF( buf ), n );
- IF r < 0 THEN
- res := Unix.errno();
- r := Unix.close( fdold );
- r := Unix.close( fdnew );
- RETURN
- END;
- n := Unix.read( fdold, ADDRESSOF( buf ), Bufsize )
- END;
- r := Unix.unlink( ADDRESSOF( old ) );
- r := Unix.close( fdold );
- r := Unix.unlink( ADDRESSOF( old ) ); (* for virtual box to work *)
- r := Unix.close( fdnew );
- res := Files.Ok
- ELSE
- RETURN (* res is Unix.rename return code *)
- END
- END;
- res := Files.Ok
- ELSE
- res := Unix.errno();
- END
- END Rename0;
-
- PROCEDURE CreateDirectory0*( path: ARRAY OF CHAR; VAR res: LONGINT );
- VAR r: LONGINT;
- BEGIN {EXCLUSIVE}
- r := Unix.mkdir( ADDRESSOF( path ), Unix.rwxrwxrwx );
- IF r = 0 THEN res := Files.Ok
- ELSE res := Unix.errno( )
- END
- END CreateDirectory0;
-
- PROCEDURE RemoveDirectory0*( path: ARRAY OF CHAR; force: BOOLEAN; VAR key, res: LONGINT );
- VAR r: LONGINT;
- BEGIN {EXCLUSIVE}
- r := Unix.rmdir( ADDRESSOF( path ) );
- IF r = 0 THEN res := Files.Ok
- ELSE res := Unix.errno( )
- END
- END RemoveDirectory0;
- PROCEDURE Enumerate0*( mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator );
- VAR
- path, filemask: Filename;
- i, j: INTEGER; dirName, fileName, fullName: Filename;
- checkSet: NameSet; ent: Unix.Dirent;
-
- PROCEDURE GetEntryName;
- VAR i: INTEGER; adr: ADDRESS;
- BEGIN
- i := -1; adr := ADDRESSOF( ent.name );
- REPEAT INC( i ); S.GET( adr, fileName[i] ); INC( adr ) UNTIL fileName[i] = 0X
- END GetEntryName;
-
- PROCEDURE EnumDir( CONST dirName: ARRAY OF CHAR );
- VAR
- dir: ADDRESS;
- tm: Unix.TmPtr; date, time: LONGINT;
- stat: Unix.Status; r: LONGINT;
- BEGIN
- dir := Unix.opendir( ADDRESSOF( dirName ) );
- IF dir # 0 THEN
- ent := Unix.readdir( dir );
- WHILE ent # NIL DO
- COPY( dirName, fullName );
- GetEntryName; AppendName( fullName, fileName );
- IF (fileName[0] # '.') & Match( fileName, filemask, 0, 0 ) THEN
- IF checkSet.Add( fileName ) THEN (* not a covered name *)
- r := Unix.stat( ADDRESSOF( fullName ), stat );
- tm := Unix.localtime( stat.mtime );
- date := tm.year*200H + (tm.mon + 1)*20H + tm.mday;
- time := tm.hour*1000H + tm.min*40H + tm.sec;
- flags := {};
- IF IsDirectory( stat ) THEN
- flags := {Files.ReadOnly, Files.Directory}
- ELSE
- r := Unix.access( ADDRESSOF( fullName ), Unix.W_OK );
- IF r < 0 THEN flags := {Files.ReadOnly} END
- END;
- enum.PutEntry( fullName, flags, time, date, stat.size );
- END
- END;
- ent := Unix.readdir( dir );
- END;
- Unix.closedir( dir )
- END;
- END EnumDir;
-
- BEGIN {EXCLUSIVE}
- Files.SplitName( mask, prefix, fullName );
- Files.SplitPath( fullName, path, filemask );
- NEW( checkSet, "M###N" );
- IF path # "" THEN
- CleanPath( path );
- EnumDir( path )
- ELSE
- i := 0; j := 0;
- LOOP
- IF (searchPath[i] = " ") OR (searchPath[i] = 0X) THEN
- dirName[j] := 0X;
- EnumDir( dirName );
- IF searchPath[i] = 0X THEN EXIT
- ELSE INC( i ); j := 0
- END
- ELSE
- dirName[j] := searchPath[i]; INC( j ); INC( i )
- END
- END
- END;
- checkSet := NIL;
- END Enumerate0;
- END UnixFileSystem;
-
-
- Buffer = POINTER TO RECORD (Files.Hint)
- chg: BOOLEAN;
- org, size: LONGINT;
- data: ARRAY Bufsize OF CHAR;
- END;
-
- File* = OBJECT (Files.File)
- VAR
- fd, fdTemp: LONGINT;
- workName, registerName: Filename;
- tempFile: BOOLEAN;
- dev: Unix.DevT;
- ino: LONGINT;
- mtime: HUGEINT;
- fsize, fpos: SIZE;
- bufs: ARRAY NBufs OF Buffer;
- swapper, state: LONGINT;
- PROCEDURE & Init( fs: Files.FileSystem );
- BEGIN
- SELF.fs := fs; flags := {}; fdTemp := 0;
- END Init;
-
- PROCEDURE CreateUnixFile;
- CONST
- CreateFlags = Unix.rdwr + Unix.creat + Unix.trunc;
- VAR
- stat: Unix.Status; done: BOOLEAN; r: LONGINT;
- BEGIN
- IF state = Create THEN
- GetTempName( registerName, workName ); tempFile := TRUE
- ELSIF state = Closed THEN
- workName := registerName; registerName := ""; tempFile := FALSE;
- END;
- r := Unix.unlink( ADDRESSOF( workName ) );
- (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*)
-
- (*IF (FileTabSize - openfiles) < ResFiles THEN Kernel.GC END;*)
-
- fd := Unix.open( ADDRESSOF( workName ), CreateFlags, Unix.rwrwr );
- done := fd >= 0; r := Unix.errno();
- IF (~done & (r IN {Unix.ENFILE, Unix.EMFILE})) THEN
- Kernel.GC; (*! + wait ? *)
- fd := Unix.open( ADDRESSOF( workName ), CreateFlags, Unix.rwrwr );
- done := fd >= 0
- END;
- IF tempFile THEN fdTemp:= fd END;
- IF done THEN
- (*IF fd >= FileTabSize THEN
- r := Unix.close( fd );
- Halt( SELF, FALSE, "UnixFiles.File.Create: too many files open" )
- ELSE*)
- r := Unix.fstat( fd, stat );
- dev := stat.dev; ino := stat.ino; mtime := stat.mtime.sec;
- state := Open; fpos := 0;
- (*fileTab[fd].f := SELF;*)
- INC( openfiles );
-
- collection.AddNew(SELF);
- (*RegisterFinalizer( SELF, Cleanup );*)
- (*END*)
- ELSE
- Halt( SELF, TRUE, "UnixFiles.File.Create: open failed" );
- END
- END CreateUnixFile;
-
-
- PROCEDURE Flush( buf: Buffer );
- VAR res: LONGINT; stat: Unix.Status;
- BEGIN
- IF buf.chg THEN
- IF fd = NoDesc THEN CreateUnixFile END;
- IF buf.org # fpos THEN res := Unix.lseek( fd, buf.org, 0 ) END;
- res := Unix.write( fd, ADDRESSOF( buf.data ), buf.size );
- IF res < 0 THEN Halt( SELF, TRUE, "UnixFiles.File.Flush: write failed" ) END;
- fpos := buf.org + buf.size; buf.chg := FALSE;
- res := Unix.fstat( fd, stat ); mtime := stat.mtime.sec
- END
- END Flush;
-
-
- PROCEDURE Set*( VAR r: Files.Rider; pos: LONGINT );
- BEGIN {EXCLUSIVE}
- SetX( r, pos )
- END Set;
-
- PROCEDURE SetX( VAR r: Files.Rider; p: LONGINT );
- VAR org, offset, i, n, res: LONGINT; buf: Buffer;
- BEGIN
- IF p > fsize THEN p := LONGINT(fsize)
- ELSIF p < 0 THEN p := 0
- END;
- offset := p MOD Bufsize; org := p - offset;
- i := 0;
- WHILE (i < NBufs) & (bufs[i] # NIL) & (org # bufs[i].org) DO INC( i ) END;
- IF i < NBufs THEN
- IF bufs[i] = NIL THEN
- NEW( buf ); buf.chg := FALSE; buf.org := -1;
- bufs[i] := buf
- ELSE
- swapper := i;
- buf := bufs[swapper]; Flush( buf )
- END
- ELSE
- swapper := (swapper + 1) MOD NBufs;
- buf := bufs[swapper]; Flush( buf )
- END;
- IF buf.org # org THEN
- IF org = fsize THEN
- buf.size := 0
- ELSE
- IF fd = NoDesc THEN CreateUnixFile END;
- IF fpos # org THEN res := Unix.lseek( fd, org, 0 ) END;
- IF res < 0 THEN Halt( SELF, TRUE, "UnixFiles.File.Set: lseek failed" ) END;
- n := Unix.read( fd, ADDRESSOF( buf.data ), Bufsize );
- IF n < 0 THEN
- IF p < fsize THEN Halt( SELF, TRUE, "UnixFiles.File.Set: read failed" )
- ELSE n := 0
- END
- END;
- fpos := org + n; buf.size := n
- END;
- buf.org := org; buf.chg := FALSE
- ELSE
- org := buf.org
- END;
- r.hint := buf; r.apos := org; r.bpos := offset;
- r.res := 0; r.eof := FALSE;
- r.file := SELF; r.fs := fs
- END SetX;
-
- PROCEDURE Pos*( VAR r: Files.Rider ): LONGINT;
- BEGIN
- RETURN r.apos + r.bpos
- END Pos;
- PROCEDURE Read*( VAR r: Files.Rider; VAR x: CHAR );
- VAR offset: LONGINT; buf: Buffer;
- BEGIN {EXCLUSIVE}
- buf := r.hint(Buffer); offset := r.bpos;
- IF r.apos # buf.org THEN
- SetX( r, r.apos + offset );
- buf := r.hint(Buffer); offset := r.bpos
- END;
- IF (offset < buf.size) THEN
- x := buf.data[offset]; r.bpos := offset + 1
- ELSIF r.apos + offset < fsize THEN
- SetX( r, r.apos + offset );
- x := r.hint(Buffer).data[0]; r.bpos := 1
- ELSE
- x := 0X; r.eof := TRUE
- END
- END Read;
- PROCEDURE ReadBytes*( VAR r: Files.Rider; VAR x: ARRAY OF CHAR; ofs, len: LONGINT );
- VAR xpos, min, restInBuf, offset: LONGINT; buf: Buffer;
- BEGIN {EXCLUSIVE}
- x[ofs] := 0X; xpos := ofs;
- buf := r.hint(Buffer); offset := r.bpos;
- WHILE len > 0 DO
- IF (r.apos # buf.org) OR (offset >= Bufsize) THEN
- SetX( r, r.apos + offset );
- buf := r.hint(Buffer); offset := r.bpos
- END;
- restInBuf := buf.size - offset;
- IF restInBuf = 0 THEN r.res := len; r.eof := TRUE; RETURN
- ELSIF len > restInBuf THEN min := restInBuf
- ELSE min := len
- END;
- S.MOVE( ADDRESSOF( buf.data ) + offset, ADDRESSOF( x ) + xpos, min );
- INC( offset, min ); r.bpos := offset;
- INC( xpos, min ); DEC( len, min )
- END;
- r.res := 0; r.eof := FALSE;
- END ReadBytes;
-
-
- PROCEDURE Write*( VAR r: Files.Rider; x: CHAR );
- VAR buf: Buffer; offset: LONGINT;
- BEGIN {EXCLUSIVE}
- buf := r.hint(Buffer); offset := r.bpos;
- IF (r.apos # buf.org) OR (offset >= Bufsize) THEN
- SetX( r, r.apos + offset );
- buf := r.hint(Buffer); offset := r.bpos
- END;
- buf.data[offset] := x; buf.chg := TRUE;
- IF offset = buf.size THEN INC( buf.size ); INC( fsize ) END;
- r.bpos := offset + 1; r.res := Files.Ok
- END Write;
- PROCEDURE WriteBytes*( VAR r: Files.Rider; CONST x: ARRAY OF CHAR; ofs, len: LONGINT );
- VAR xpos, min, restInBuf, offset: LONGINT; buf: Buffer;
- BEGIN {EXCLUSIVE}
- xpos := ofs; buf := r.hint(Buffer); offset := r.bpos;
- WHILE len > 0 DO
- IF (r.apos # buf.org) OR (offset >= Bufsize) THEN
- SetX( r, r.apos + offset );
- buf := r.hint(Buffer); offset := r.bpos
- END;
- restInBuf := Bufsize - offset;
- IF len > restInBuf THEN min := restInBuf ELSE min := len END;
- S.MOVE( ADDRESSOF( x ) + xpos, ADDRESSOF( buf.data ) + offset, min );
- INC( offset, min ); r.bpos := offset;
- IF offset > buf.size THEN
- INC( fsize, offset - buf.size ); buf.size := offset
- END;
- INC( xpos, min ); DEC( len, min ); buf.chg := TRUE
- END;
- r.res := Files.Ok
- END WriteBytes;
-
- PROCEDURE Length*( ): LONGINT;
- BEGIN
- RETURN LONGINT(fsize)
- END Length;
- PROCEDURE GetDate*( VAR t, d: LONGINT );
- VAR stat: Unix.Status; r: LONGINT; time: Unix.TmPtr;
- BEGIN {EXCLUSIVE}
- IF fd = NoDesc THEN CreateUnixFile END;
- r := Unix.fstat( fd, stat );
- time := Unix.localtime( stat.mtime );
- t := time.sec + ASH( time.min, 6 ) + ASH( time.hour, 12 );
- d := time.mday + ASH( time.mon + 1, 5 ) + ASH( time.year, 9 );
- END GetDate;
- PROCEDURE SetDate*( t, d: LONGINT );
- TYPE
- Time = RECORD actime, modtime: LONGINT END;
- VAR
- tm: Unix.Tm; buf: Time; r: LONGINT; path: Filename;
- BEGIN {EXCLUSIVE}
- IF registerName # "" THEN COPY( registerName, path )
- ELSE COPY( workName, path )
- END;
- (* get year and timezone *)
- (* fill in new date *)
- tm.isdst := -1; tm.sec := t MOD 64; tm.min := t DIV 64 MOD 64;
- tm.hour := t DIV 4096 MOD 32;
- tm.mday := d MOD 32; tm.mon := d DIV 32 MOD 16 - 1; tm.year := d DIV 512;
- tm.wday := 0; tm.yday := 0;
- buf.actime := Unix.mktime( tm ); buf.modtime := buf.actime;
- r := Unix.utime( ADDRESSOF( path ), ADDRESSOF( buf ) );
- END SetDate;
- PROCEDURE GetAttributes*( ): SET;
- BEGIN {EXCLUSIVE}
- RETURN flags
- END GetAttributes;
- PROCEDURE SetAttributes*( attr: SET );
- BEGIN {EXCLUSIVE}
- (* flags := attr *)
- END SetAttributes;
- PROCEDURE Register0*( VAR res: LONGINT );
- BEGIN {EXCLUSIVE}
- IF (state = Create) & (registerName # "") THEN
- state := Closed (* shortcut renaming *) ;
- END;
- FlushBuffers;
- IF registerName # "" THEN
- fs.Rename0( workName, registerName, SELF, res );
- IF res # Files.Ok THEN
- Halt( SELF, FALSE, "UnixFiles.File.Register: rename failed" )
- END;
-
- IF tempFile & (fdTemp # 0) THEN
- res := Unix.close(fdTemp);
- res := Unix.unlink( ADDRESSOF( workName ) );
- fdTemp := 0;
- END;
-
- workName := registerName; registerName := ""; tempFile := FALSE;
- END;
- (*collection.Register(SELF);*)
- END Register0;
-
- PROCEDURE Update*;
- BEGIN {EXCLUSIVE}
- FlushBuffers
- END Update;
-
-
- PROCEDURE FlushBuffers;
- VAR i: LONGINT;
- BEGIN
- IF fd = NoDesc THEN CreateUnixFile END;
- FOR i := 0 TO NBufs - 1 DO
- IF bufs[i] # NIL THEN Flush( bufs[i] ) END
- END;
- END FlushBuffers;
-
-
- PROCEDURE Finalize*;
- VAR r: LONGINT;
- BEGIN {EXCLUSIVE}
- IF tempFile THEN
- IF fd # NoDesc THEN r := Unix.close(fd) END;
- r := Unix.unlink( ADDRESSOF( registerName ) );
- fd := NoDesc;
- ELSE
- FlushBuffers;
- IF fd # NoDesc THEN
- r := Unix.close( fd );
- fd := NoDesc;
- END;
- END;
- DEC( openfiles ); state := Closed
- END Finalize;
-
- PROCEDURE Close;
- BEGIN
- Finalize;
- collection.oldFiles.Remove(SELF);
- END Close;
-
-
- PROCEDURE GetName*( VAR name: ARRAY OF CHAR );
- BEGIN {EXCLUSIVE}
- IF registerName = "" THEN COPY( workName, name ) ;
- ELSE COPY( registerName, name )
- END;
- CleanPath( name )
- END GetName;
- END File;
- (*===================================================================*)
- (** Get the current directory. *)
- PROCEDURE GetWorkingDirectory*( VAR path: ARRAY OF CHAR );
- BEGIN
- COPY( cwd, path )
- END GetWorkingDirectory;
-
- (** Change to directory path. *)
- PROCEDURE ChangeDirectory*( CONST path: ARRAY OF CHAR; VAR done: BOOLEAN );
- VAR r: LONGINT; newdir: Filename;
- BEGIN
- IF path[0] # '/' THEN
- COPY( cwd, newdir ); AppendName( newdir, path );
- CleanPath( newdir )
- ELSE
- COPY( path, newdir );
- END;
- r := Unix.chdir( ADDRESSOF( newdir ) );
- IF r = 0 THEN COPY( newdir, cwd ); done := TRUE ELSE done := FALSE END
- END ChangeDirectory;
- (*===================================================================*)
-
- PROCEDURE StripPath*( CONST path: ARRAY OF CHAR; VAR name: ARRAY OF CHAR );
- VAR i, p: INTEGER; c: CHAR;
- BEGIN
- i := 0; p := 0;
- REPEAT
- IF path[i] = '/' THEN p := i + 1 END;
- INC( i )
- UNTIL path[i] = 0X;
- i := 0;
- REPEAT c := path[p]; name[i] := c; INC( i ); INC( p ) UNTIL c = 0X
- END StripPath;
-
-
- PROCEDURE CleanPath*( VAR path: ARRAY OF CHAR );
- (*
- /aaa/../bbb/./ccc/../ddd/. ==> /bbb/ddd
- ../aaa ==> CWD/../aaa ==> . . .
- *)
- VAR
- i, prevNameStart, nameStart: INTEGER;
- c1, c2, c3: CHAR;
- PROCEDURE prependCWD;
- VAR tmp: ARRAY 256 OF CHAR;
- BEGIN
- COPY( cwd, tmp ); AppendName( tmp, path ); COPY( tmp, path )
- END prependCWD;
-
- PROCEDURE restart;
- BEGIN
- IF path[0] = '/' THEN nameStart := 1 ELSE nameStart := 0 END;
- i := -1; prevNameStart := -1;
- END restart;
- PROCEDURE shift( p0, p1: INTEGER );
- VAR c: CHAR;
- BEGIN
- REPEAT c := path[p1]; path[p0] := c; INC( p0 ); INC( p1 ) UNTIL c = 0X;
- IF p0 > 1 THEN restart ELSE i := 0 END
- END shift;
- BEGIN
- restart;
- REPEAT
- INC( i );
- IF i = nameStart THEN
- c1 := path[i]; c2 := path[i + 1]; c3 := path[i + 2];
- IF c1 = '/' THEN shift( i, i + 1 ) (* // *)
- ELSIF c1 = '.' THEN
- IF c2 = 0X THEN
- IF i > 1 THEN DEC( i ) END;
- path[i] := 0X
- ELSIF c2 = '/' THEN shift( i, i + 2 ); (* ./ *)
- ELSIF (c2 = '.') & ((c3 = 0X) OR (c3 = '/')) THEN (* .. *)
- IF i = 0 THEN prependCWD; restart
- ELSIF c3 = 0X THEN DEC( i ); path[i] := 0X
- ELSIF c3 = '/' THEN (* ../ *)
- IF prevNameStart >= 0 THEN shift( prevNameStart, i + 3 ) END
- END
- END
- END
- ELSIF path[i] = '/' THEN
- IF i > 0 THEN prevNameStart := nameStart END;
- nameStart := i + 1
- END;
- UNTIL (i >= 0) & (path[i] = 0X);
- IF (i > 1) & (path[i - 1] = '/') THEN path[i - 1] := 0X END;
- IF path = "" THEN path := "." END;
- END CleanPath;
- PROCEDURE Match( CONST name, pat: ARRAY OF CHAR; i, j: INTEGER ): BOOLEAN;
- BEGIN
- IF (name[i] = 0X) & (pat[j] = 0X) THEN RETURN TRUE
- ELSIF pat[j] # "*" THEN RETURN (name[i] = pat[j]) & Match( name, pat, i + 1, j + 1 )
- ELSE (* pat[j] = "*", name[i] may be 0X *)
- RETURN Match( name, pat, i, j + 1 ) OR ((name[i] # 0X) & Match( name, pat, i + 1, j ))
- END
- END Match;
-
-
- PROCEDURE Append( VAR a: Filename; CONST this: ARRAY OF CHAR );
- VAR i, j: LONGINT;
- BEGIN
- i := 0; j := 0;
- WHILE a[i] # 0X DO INC( i ) END;
- WHILE (i < LEN( a ) - 1) & (this[j] # 0X) DO a[i] := this[j]; INC( i ); INC( j ) END;
- a[i] := 0X
- END Append;
- PROCEDURE AppendName( VAR path: Filename; CONST filename: ARRAY OF CHAR );
- VAR i, j, max: LONGINT;
- BEGIN
- i := 0; j := 0; max := LEN( path ) - 1;
- WHILE path[i] # 0X DO INC( i ) END;
- IF (i > 0) & (path[i - 1] # "/") THEN path[i] := "/"; INC( i ); path[i] := 0X END;
- Append( path, filename );
- END AppendName;
-
-
- PROCEDURE AppendInt( VAR str: Filename; n: LONGINT );
- VAR i: LONGINT;
- BEGIN
- i := 0;
- WHILE str[i] # 0X DO INC(i) END;
- WHILE n > 0 DO str[i] := CHR( n MOD 10 + ORD('0') ); n := n DIV 10; INC(i) END;
- str[i] := 0X
- END AppendInt;
- PROCEDURE IsFullName( CONST name: ARRAY OF CHAR ): BOOLEAN;
- VAR i: INTEGER; ch: CHAR;
- BEGIN
- i := 0; ch := name[0];
- WHILE (ch # 0X) & (ch # "/") DO INC( i ); ch := name[i] END;
- RETURN ch = "/"
- END IsFullName;
- PROCEDURE Halt( f: File; unixError: BOOLEAN; CONST msg: ARRAY OF CHAR );
- VAR fd, errno: LONGINT;
- workName, registerName: Filename;
- BEGIN
- IF f = NIL THEN
- workName := "???"; registerName := "???"
- ELSE
- workName := f.workName; registerName := f.registerName; fd := f.fd
- END;
- IF unixError THEN errno := Unix.errno( ); Unix.Perror( msg ) END;
- HALT( 99 )
- END Halt;
- (*
- PROCEDURE RegisterFinalizer( obj: ANY; fin: Heaps.Finalizer );
- VAR n: Heaps.FinalizerNode;
- BEGIN
- NEW( n ); n.finalizer := fin; Heaps.AddFinalizer( obj, n );
- END RegisterFinalizer;
- *)
- (*
- PROCEDURE GC;
- BEGIN
- Kernel.GC;
- AwaitFinalizingDone
- END GC;
-
- PROCEDURE AwaitFinalizingDone;
- BEGIN
- (* wait until finalizers have finished! (Cleanup)*)
- WHILE Machine.GCacquired DO Objects.Sleep( 10 ) END
- END AwaitFinalizingDone;
- *)
- PROCEDURE ResetBuffers( f: File; VAR stat: Unix.Status );
- VAR i: INTEGER;
- BEGIN
- f.fsize := stat.size;
- IF (f.mtime # stat.mtime.sec) THEN
- FOR i := 0 TO NBufs - 1 DO
- IF f.bufs[i] # NIL THEN f.bufs[i].org := -1; f.bufs[i] := NIL END;
- END;
- f.swapper := -1; f.mtime := stat.mtime.sec
- END
- END ResetBuffers;
- (*
- PROCEDURE FindCachedEntry( VAR stat: Unix.Status ): File;
- VAR f: File; i: INTEGER;
- BEGIN
- FOR i := 0 TO FileTabSize - 1 DO
- f := fileTab[i].f;
- IF (f # NIL ) & (stat.ino = f.ino) & (stat.dev = f.dev) THEN
- (* possible different name but same file! *)
- ResetBuffers( f, stat );
- RETURN f
- END;
- END;
- RETURN NIL
- END FindCachedEntry;
- *)
- PROCEDURE MakePath( CONST dir, name: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR );
- VAR i, j: INTEGER;
- BEGIN
- i := 0; j := 0;
- WHILE dir[i] # 0X DO dest[i] := dir[i]; INC( i ) END;
- IF (i>0) & (dest[i - 1] # "/") THEN dest[i] := "/"; INC( i ) END;
- WHILE name[j] # 0X DO dest[i] := name[j]; INC( i ); INC( j ) END;
- dest[i] := 0X
- END MakePath;
-
-
- PROCEDURE ScanPath( VAR pos: LONGINT; VAR dir: ARRAY OF CHAR );
- VAR i: LONGINT; ch: CHAR;
- BEGIN
- i := 0; ch := searchPath[pos];
- WHILE ch = " " DO INC( pos ); ch := searchPath[pos] END;
- WHILE ch > " " DO dir[i] := ch; INC( i ); INC( pos ); ch := searchPath[pos] END;
- dir[i] := 0X
- END ScanPath;
- PROCEDURE GetTempName( CONST finalName: ARRAY OF CHAR; VAR tempName: Filename );
- VAR n, i, j, pe, pid: LONGINT;
- BEGIN
- INC(tempno); n := tempno; i := 0; j := 0; pe := 1;
- WHILE finalName[j] = ' ' DO INC(j) END; (* skip leading spaces *)
- IF finalName[j] # "/" THEN (* relative pathname *)
- WHILE cwd[i] # 0X DO tempName[i] := cwd[i]; INC(i) END;
- IF tempName[i - 1] # '/' THEN tempName[i] := '/'; INC(i) END;
- pe := i - 1
- END;
- WHILE finalName[j] # 0X DO tempName[i] := finalName[j]; INC(i); INC(j) END;
- WHILE (i > pe) & (tempName[i-1] # '/') DO DEC(i) END; (* remove filename *)
- tempName[i] := 0X;
- Append( tempName, ".tmp." );
- AppendInt( tempName, n ); Append( tempName, "." );
- pid := Unix.getpid();
- AppendInt( tempName, pid )
- END GetTempName;
-
-
- PROCEDURE Cleanup( obj: ANY );
- VAR f: File;
- BEGIN
- f := S.VAL( File, obj ); f.Close
- END Cleanup;
- (*
- PROCEDURE CloseFiles;
- VAR i: LONGINT; f: File;
- BEGIN
- i := 0;
- WHILE i < FileTabSize DO
- f := fileTab[i].f;
- IF f # NIL THEN f.Close END;
- INC( i )
- END;
- END CloseFiles;
- *)
- PROCEDURE Install;
- VAR aliasFS: AliasFileSystem;
- BEGIN
- NEW(collection);
- NEW( unixFS ); (* Files.Add( unixFS, "" ); *)
- NEW( aliasFS, unixFS ); Files.Add( aliasFS, "searcher" )
- END Install;
-
- PROCEDURE Initialize;
- VAR a: ADDRESS; i: INTEGER; ch: CHAR;
- BEGIN
- (* get current working directory *)
- a := Unix.getenv( ADDRESSOF( "PWD" ) );
- IF a > 0 THEN
- i := 0;
- REPEAT S.GET( a, ch ); INC( a ); cwd[i] := ch; INC( i ) UNTIL ch = 0X;
- ELSE
- (* $PWD not set *)
- a := Unix.getcwd( ADDRESSOF( cwd ), LEN( cwd ) )
- END;
- i := 0;
- WHILE cwd[i] # 0X DO INC( i ) END;
- DEC( i );
- IF (i > 0) & (cwd[i] = '/') THEN cwd[i] := 0X END;
- (* get search pathes *)
- a := Unix.getenv( ADDRESSOF( "AOSPATH" ) ); i := 0;
- IF a = 0 THEN
- Log.String( "UnixFiles.Initialize: environment variable AOSPATH not defined" ); Log.Ln;
- (* Unix.exit( 1 ) *)
- ELSE
- REPEAT
- S.GET( a, ch ); INC( a );
- IF ch = ":" THEN ch := " " END;
- searchPath[i] := ch; INC( i )
- UNTIL ch = 0X;
- END;
- i := 0;
- (*
- WHILE i < DO fileTab[i].f := NIL; INC( i ) END;
- *)
- tempno := 1; openfiles := 0;
- Modules.InstallTermHandler( Finalization )
- END Initialize;
- PROCEDURE Finalization;
- VAR ft: Files.FileSystemTable; i: LONGINT;
- BEGIN
- Files.GetList( ft );
- IF ft # NIL THEN
- FOR i := 0 TO LEN( ft^ ) - 1 DO
- IF ft[i] IS AliasFileSystem THEN Files.Remove( ft[i] ) END
- END
- END;
- collection.Finalize;
- unixFS.Finalize;
- END Finalization;
- BEGIN
- Initialize;
- Install
- END UnixFiles.
|