Generic.Unix.UnixFiles.Mod 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE UnixFiles; (** AUTHOR "gf"; PURPOSE "Unix file systems" *)
  3. (* derived fron (SPARCOberon) Files.Mod by J. Templ 1.12. 89/14.05.93 *)
  4. IMPORT S := SYSTEM, Unix, Machine, Heaps, Objects, Kernel, Modules, Log := KernelLog, Files;
  5. CONST
  6. NBufs = 4; Bufsize = 4096; FileTabSize = 1024; ResFiles = 128; NoDesc = -1;
  7. Open = 0; Create = 1; Closed = 2; (* file states *)
  8. NoKey = -1;
  9. TraceCollection = 0;
  10. TraceSearch=1;
  11. Trace = {};
  12. VAR
  13. (*fileTab: ARRAY FileTabSize OF RECORD f {UNTRACED}: File END;
  14. *)
  15. tempno: INTEGER;
  16. openfiles: INTEGER;
  17. searchPath: ARRAY 1024 OF CHAR;
  18. cwd: ARRAY 256 OF CHAR;
  19. unixFS: UnixFileSystem; (* must be unique *)
  20. collection: Collection; (* must be unique *)
  21. TYPE
  22. Filename = ARRAY 256 OF CHAR;
  23. NameSet = OBJECT
  24. VAR
  25. name: ARRAY 64 OF CHAR;
  26. left, right: NameSet;
  27. PROCEDURE Add( CONST filename: ARRAY OF CHAR ): BOOLEAN;
  28. (* add filename if it not already exists. else return false *)
  29. BEGIN
  30. IF filename = name THEN RETURN FALSE END;
  31. IF filename < name THEN
  32. IF left = NIL THEN NEW( left, filename ); RETURN TRUE
  33. ELSE RETURN left.Add( filename )
  34. END
  35. ELSE
  36. IF right = NIL THEN NEW( right, filename ); RETURN TRUE
  37. ELSE RETURN right.Add( filename )
  38. END
  39. END
  40. END Add;
  41. PROCEDURE & Init( CONST filename: ARRAY OF CHAR );
  42. BEGIN
  43. COPY( filename, name );
  44. left := NIL; right := NIL
  45. END Init;
  46. END NameSet;
  47. AliasFileSystem = OBJECT (Files.FileSystem)
  48. VAR
  49. fs: UnixFileSystem;
  50. PROCEDURE & Init*( realFS: UnixFileSystem);
  51. BEGIN
  52. SELF.fs := realFS;
  53. END Init;
  54. PROCEDURE New0( name: ARRAY OF CHAR ): Files.File;
  55. VAR f: Files.File;
  56. BEGIN
  57. f := fs.New0( name );
  58. IF f # NIL THEN f.fs := SELF END;
  59. RETURN f;
  60. END New0;
  61. PROCEDURE Old0( name: ARRAY OF CHAR ): Files.File;
  62. VAR f: Files.File;
  63. BEGIN
  64. f := fs.Old0( name );
  65. IF f # NIL THEN f.fs := SELF END;
  66. RETURN f;
  67. END Old0;
  68. PROCEDURE Delete0( name: ARRAY OF CHAR; VAR key, res: LONGINT );
  69. BEGIN
  70. fs.Delete0( name, key, res );
  71. END Delete0;
  72. PROCEDURE Rename0( old, new: ARRAY OF CHAR; fold: Files.File; VAR res: LONGINT );
  73. BEGIN
  74. fs.Rename0( old, new, fold, res );
  75. END Rename0;
  76. PROCEDURE Enumerate0( mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator );
  77. BEGIN
  78. fs.Enumerate0( mask, flags, enum );
  79. END Enumerate0;
  80. PROCEDURE FileKey( name: ARRAY OF CHAR ): LONGINT;
  81. VAR
  82. BEGIN
  83. RETURN fs.FileKey( name );
  84. END FileKey;
  85. PROCEDURE CreateDirectory0( name: ARRAY OF CHAR; VAR res: LONGINT );
  86. BEGIN
  87. fs.CreateDirectory0( name, res );
  88. END CreateDirectory0;
  89. PROCEDURE RemoveDirectory0( name: ARRAY OF CHAR; force: BOOLEAN; VAR key, res: LONGINT );
  90. BEGIN
  91. fs.RemoveDirectory0( name, force, key, res );
  92. END RemoveDirectory0;
  93. END AliasFileSystem;
  94. FinalizeFiles = OBJECT
  95. PROCEDURE EnumFile( f: ANY; VAR cont: BOOLEAN );
  96. VAR F: File;
  97. BEGIN
  98. F := f( File ); F.Finalize(); cont := TRUE
  99. END EnumFile;
  100. END FinalizeFiles;
  101. SearchByName = OBJECT
  102. VAR sname: Filename;
  103. found: File;
  104. PROCEDURE Init( name: ARRAY OF CHAR );
  105. BEGIN
  106. found := NIL; COPY(name, sname); (* UpperCase( name, sname )*)
  107. END Init;
  108. PROCEDURE EnumFile( f: ANY; VAR cont: BOOLEAN );
  109. VAR F: File; fname: Filename;
  110. BEGIN
  111. F := f( File ); (* UpperCase( F.workName, fname );*)
  112. IF TraceSearch IN Trace THEN Log.String( "Enumerate: " ); Log.String( fname );
  113. END;
  114. IF sname = fname THEN found := F; cont := FALSE ELSE cont := TRUE END;
  115. IF TraceSearch IN Trace THEN
  116. IF cont THEN Log.String( " # " ); ELSE Log.String( " = " ); END;
  117. Log.String( sname ); Log.Ln;
  118. END;
  119. END EnumFile;
  120. END SearchByName;
  121. SearchByFstat = OBJECT
  122. VAR
  123. found: File;
  124. stat: Unix.Status;
  125. PROCEDURE Init( s: Unix.Status );
  126. BEGIN
  127. found := NIL;
  128. stat := s;
  129. END Init;
  130. PROCEDURE EnumFile( f: ANY; VAR cont: BOOLEAN );
  131. VAR F: File; fname: Filename;
  132. BEGIN
  133. WITH f: File DO
  134. IF (f # NIL ) & (stat.ino = f.ino) & (stat.dev = f.dev) THEN
  135. (* possible different name but same file! *)
  136. ResetBuffers( f, stat );
  137. found := f; cont := FALSE;
  138. END;
  139. END;
  140. END EnumFile;
  141. END SearchByFstat;
  142. Collection = OBJECT (* methods in Collection shared by objects Filesystem and File *)
  143. VAR oldFiles, newFiles: Kernel.FinalizedCollection;
  144. search: SearchByName;
  145. ssearch: SearchByFstat;
  146. fileKey: LONGINT;
  147. PROCEDURE & Init*;
  148. BEGIN
  149. NEW( oldFiles ); NEW( newFiles ); NEW( search ); NEW(ssearch); fileKey := -1;
  150. END Init;
  151. PROCEDURE GetNextFileKey( ): LONGINT;
  152. BEGIN {EXCLUSIVE}
  153. DEC( fileKey ); RETURN fileKey
  154. END GetNextFileKey;
  155. PROCEDURE Register( F: File );
  156. BEGIN {EXCLUSIVE}
  157. IF TraceCollection IN Trace THEN Log.String( "Collections.Register " ); Log.String( F.workName ); Log.Ln; END;
  158. oldFiles.Add( F, FinalizeFile ); newFiles.Remove( F ); DEC( fileKey ); (*F.Init( F.workName, F.hfile, fileKey,F.fileSystem );*)
  159. END Register;
  160. PROCEDURE Unregister( F: File );
  161. BEGIN {EXCLUSIVE}
  162. IF TraceCollection IN Trace THEN Log.String( "Unregister " ); Log.String( F.workName ); Log.Ln; END;
  163. oldFiles.Remove( F ); newFiles.Add( F, FinalizeFile (* FinalizeFile*) ); (*F.Init( F.workName, Kernel32.InvalidHandleValue, 0, F.fileSystem );*)
  164. END Unregister;
  165. PROCEDURE AddNew( F: File );
  166. BEGIN {EXCLUSIVE}
  167. IF TraceCollection IN Trace THEN Log.String( "Collections.AddNew: " ); Log.String( F.workName ); Log.Ln; END;
  168. newFiles.Add( F, FinalizeFile );
  169. END AddNew;
  170. PROCEDURE AddOld( F: File );
  171. BEGIN {EXCLUSIVE}
  172. IF TraceCollection IN Trace THEN Log.String( "Collections.AddOld: " ); Log.String( F.workName ); Log.Ln; END;
  173. oldFiles.Add( F, FinalizeFile );
  174. END AddOld;
  175. PROCEDURE ByStat(CONST stat: Unix.Status): File;
  176. BEGIN
  177. ssearch.Init(stat); oldFiles.Enumerate(ssearch.EnumFile);
  178. IF TraceCollection IN Trace THEN
  179. Log.String( "Collections.ByStatus: " ); Log.Ln;
  180. IF ssearch.found = NIL THEN Log.String("not found") ELSE Log.String("found") END;
  181. END;
  182. RETURN ssearch.found;
  183. END ByStat;
  184. PROCEDURE ByName( VAR fname: ARRAY OF CHAR ): File;
  185. BEGIN {EXCLUSIVE}
  186. IF TraceCollection IN Trace THEN Log.String( "Collections.ByName: " ); Log.String( fname ); Log.Ln; END;
  187. search.Init( fname ); oldFiles.Enumerate( search.EnumFile ); RETURN search.found
  188. END ByName;
  189. PROCEDURE ByNameNotGC( VAR fname: ARRAY OF CHAR ): File;
  190. BEGIN {EXCLUSIVE}
  191. IF TraceCollection IN Trace THEN Log.String( "Collections.ByName: " ); Log.String( fname ); Log.Ln; END;
  192. search.Init( fname ); oldFiles.EnumerateN( search.EnumFile ); RETURN search.found;
  193. END ByNameNotGC;
  194. PROCEDURE Finalize;
  195. VAR fin: FinalizeFiles;
  196. BEGIN {EXCLUSIVE}
  197. IF TraceCollection IN Trace THEN Log.String( "Collections.Finalize " ); Log.Ln; END;
  198. NEW( fin ); newFiles.Enumerate( fin.EnumFile ); newFiles.Clear(); oldFiles.Enumerate( fin.EnumFile ); oldFiles.Clear();
  199. END Finalize;
  200. PROCEDURE FinalizeFile( obj: ANY );
  201. VAR F: File;
  202. BEGIN
  203. F := obj( File );
  204. IF TraceCollection IN Trace THEN Log.String( "Collections.FinalizeFile " ); Log.String( F.workName ); Log.Ln; END;
  205. F.Finalize()
  206. END FinalizeFile;
  207. END Collection;
  208. UnixFileSystem* = OBJECT (Files.FileSystem)
  209. PROCEDURE & Init;
  210. BEGIN
  211. prefix := ""; vol := NIL; desc := "UnixFS"
  212. END Init;
  213. PROCEDURE New0*( name: ARRAY OF CHAR ): Files.File;
  214. VAR f: File;
  215. BEGIN {EXCLUSIVE}
  216. (*AwaitFinalizingDone;*)
  217. NEW( f, SELF );
  218. f.workName := ""; COPY( name, f.registerName );
  219. f.fd := NoDesc; f.state := Create; f.fsize := 0; f.fpos := 0;
  220. f.swapper := -1; (*all f.buf[i] = NIL*)
  221. f.key := NoKey; f.fs := SELF;
  222. collection.AddNew(f);
  223. RETURN f
  224. END New0;
  225. PROCEDURE IsDirectory( VAR stat: Unix.Status ): BOOLEAN;
  226. VAR mode: LONGINT;
  227. BEGIN
  228. mode := stat.mode;
  229. RETURN ODD( mode DIV 4000H )
  230. END IsDirectory;
  231. PROCEDURE Old0*( name: ARRAY OF CHAR ): Files.File;
  232. VAR f: File; stat: Unix.Status; fd, r, errno, pos: LONGINT;
  233. oflags: SET; nextdir, path: Filename;
  234. BEGIN {EXCLUSIVE}
  235. IF name = "" THEN RETURN NIL END;
  236. IF IsFullName( name ) THEN
  237. COPY( name, path ); nextdir := "";
  238. ELSE
  239. pos := 0; ScanPath( pos, nextdir ); MakePath( nextdir, name, path );
  240. ScanPath( pos, nextdir )
  241. END;
  242. IF (FileTabSize - openfiles) < ResFiles THEN (*! GC *) END;
  243. LOOP
  244. r := Unix.access( ADDRESSOF( path ), Unix.R_OK );
  245. IF r >= 0 THEN
  246. r := Unix.access( ADDRESSOF( path ), Unix.W_OK );
  247. IF r < 0 THEN oflags := Unix.rdonly ELSE oflags := Unix.rdwr END;
  248. fd := Unix.open( ADDRESSOF( path ), oflags, {} ); errno := Unix.errno();
  249. IF ((fd < 0) & (errno IN {Unix.ENFILE, Unix.EMFILE})) OR (fd >= FileTabSize) THEN
  250. IF fd > 0 THEN r := Unix.close( fd ) END;
  251. (*!GC ;*)
  252. fd := Unix.open( ADDRESSOF( path ), oflags, {} ); errno := Unix.errno();
  253. END;
  254. IF fd >= 0 THEN
  255. r := Unix.fstat( fd, stat );
  256. f := collection.ByStat(stat);
  257. (*f := FindCachedEntry( stat );*)
  258. IF f # NIL THEN
  259. (* use the file already cached *) r := Unix.close( fd ); EXIT
  260. ELSIF fd < FileTabSize THEN
  261. (*AwaitFinalizingDone;*)
  262. NEW( f, SELF );
  263. f.fd := fd; f.dev := stat.dev; f.ino := stat.ino;
  264. f.mtime := stat.mtime.sec; f.fsize := stat.size; f.fpos := 0;
  265. f.state := Open; f.swapper := -1; (*all f.buf[i] = NIL*)
  266. COPY( path, f.workName ); f.registerName := "";
  267. f.tempFile := FALSE;
  268. IF IsDirectory( stat ) THEN
  269. f.flags := {Files.Directory, Files.ReadOnly}
  270. ELSIF oflags = Unix.rdonly THEN
  271. f.flags := {Files.ReadOnly}
  272. END;
  273. f.key := NoKey; f.fs := SELF;
  274. (*fileTab[fd].f := f; (* cache file *)*)
  275. INC( openfiles );
  276. collection.AddOld(f);
  277. (*RegisterFinalizer( f, Cleanup );*)
  278. EXIT
  279. ELSE
  280. r := Unix.close( fd );
  281. Halt( f, FALSE, "UnixFiles.File.Old0: too many files open" );
  282. END
  283. END
  284. ELSIF nextdir # "" THEN
  285. MakePath( nextdir, name, path ); ScanPath( pos, nextdir );
  286. ELSE
  287. f := NIL; EXIT
  288. END;
  289. END; (* loop *)
  290. RETURN f
  291. END Old0;
  292. (** Return the unique non-zero key of the named file, if it exists. *)
  293. PROCEDURE FileKey*( name: ARRAY OF CHAR ): LONGINT;
  294. (* Can not be used for Unix files as LONGINT is too small.
  295. In the Unix filesystem a file is identified by
  296. - dev (64 bit (Linux), 32 bit (Solaris, Darwin)) +
  297. - ino (32 bit)
  298. *)
  299. BEGIN
  300. RETURN 0
  301. END FileKey;
  302. PROCEDURE Delete0*( name: ARRAY OF CHAR; VAR key, res: LONGINT );
  303. VAR r: LONGINT;
  304. BEGIN {EXCLUSIVE}
  305. r := Unix.unlink( ADDRESSOF( name ) );
  306. IF r = 0 THEN res := Files.Ok
  307. ELSE res := Unix.errno( )
  308. END;
  309. key := 0;
  310. END Delete0;
  311. PROCEDURE Rename0*( old, new: ARRAY OF CHAR; f: Files.File; VAR res: LONGINT );
  312. CONST Bufsize = 4096;
  313. VAR fdold, fdnew, n, r: LONGINT; ostat, nstat: Unix.Status;
  314. buf: ARRAY Bufsize OF CHAR;
  315. BEGIN {EXCLUSIVE}
  316. r:= Unix.stat( ADDRESSOF( old ), ostat );
  317. IF r >= 0 THEN
  318. r := Unix.stat( ADDRESSOF( new ), nstat );
  319. IF (r >= 0) & (ostat.dev # nstat.dev) OR (ostat.ino # nstat.ino) THEN
  320. r := Unix.unlink( ADDRESSOF( new ) ) (* work around stale nfs handles *)
  321. END;
  322. r := Unix.rename( ADDRESSOF( old ), ADDRESSOF( new ) );
  323. IF r < 0 THEN
  324. res := Unix.errno( );
  325. IF res = Unix.EXDEV THEN (* cross device link, move the file *)
  326. fdold := Unix.open( ADDRESSOF( old ), Unix.rdonly, {} );
  327. IF fdold < 0 THEN
  328. res := Unix.errno( ); RETURN
  329. END;
  330. fdnew := Unix.open( ADDRESSOF( new ), Unix.rdwr + Unix.creat + Unix.trunc, Unix.rwrwr );
  331. IF fdnew < 0 THEN
  332. res := Unix.errno( ); RETURN
  333. END;
  334. n := Unix.read( fdold, ADDRESSOF( buf ), Bufsize );
  335. WHILE n > 0 DO
  336. r := Unix.write( fdnew, ADDRESSOF( buf ), n );
  337. IF r < 0 THEN
  338. res := Unix.errno();
  339. r := Unix.close( fdold ); r := Unix.close( fdnew );
  340. RETURN
  341. END;
  342. n := Unix.read( fdold, ADDRESSOF( buf ), Bufsize )
  343. END;
  344. r := Unix.unlink( ADDRESSOF( old ) );
  345. r := Unix.close( fdold ); r := Unix.close( fdnew );
  346. res := Files.Ok
  347. ELSE
  348. RETURN (* res is Unix.rename return code *)
  349. END
  350. END;
  351. res := Files.Ok
  352. ELSE
  353. res := Unix.errno()
  354. END
  355. END Rename0;
  356. PROCEDURE CreateDirectory0*( path: ARRAY OF CHAR; VAR res: LONGINT );
  357. VAR r: LONGINT;
  358. BEGIN {EXCLUSIVE}
  359. r := Unix.mkdir( ADDRESSOF( path ), Unix.rwxrwxrwx );
  360. IF r = 0 THEN res := Files.Ok
  361. ELSE res := Unix.errno( )
  362. END
  363. END CreateDirectory0;
  364. PROCEDURE RemoveDirectory0*( path: ARRAY OF CHAR; force: BOOLEAN; VAR key, res: LONGINT );
  365. VAR r: LONGINT;
  366. BEGIN {EXCLUSIVE}
  367. r := Unix.rmdir( ADDRESSOF( path ) );
  368. IF r = 0 THEN res := Files.Ok
  369. ELSE res := Unix.errno( )
  370. END
  371. END RemoveDirectory0;
  372. PROCEDURE Enumerate0*( mask: ARRAY OF CHAR; flags: SET; enum: Files.Enumerator );
  373. VAR
  374. path, filemask: Filename;
  375. i, j: INTEGER; dirName, fileName, fullName: Filename;
  376. checkSet: NameSet; ent: Unix.Dirent;
  377. PROCEDURE GetEntryName;
  378. VAR i: INTEGER; adr: ADDRESS;
  379. BEGIN
  380. i := -1; adr := ADDRESSOF( ent.name );
  381. REPEAT INC( i ); S.GET( adr, fileName[i] ); INC( adr ) UNTIL fileName[i] = 0X
  382. END GetEntryName;
  383. PROCEDURE EnumDir( CONST dirName: ARRAY OF CHAR );
  384. VAR
  385. dir: ADDRESS;
  386. tm: Unix.TmPtr; date, time: LONGINT;
  387. stat: Unix.Status; r: LONGINT;
  388. BEGIN
  389. dir := Unix.opendir( ADDRESSOF( dirName ) );
  390. IF dir # 0 THEN
  391. ent := Unix.readdir( dir );
  392. WHILE ent # NIL DO
  393. COPY( dirName, fullName );
  394. GetEntryName; AppendName( fullName, fileName );
  395. IF (fileName[0] # '.') & Match( fileName, filemask, 0, 0 ) THEN
  396. IF checkSet.Add( fileName ) THEN (* not a covered name *)
  397. r := Unix.stat( ADDRESSOF( fullName ), stat );
  398. tm := Unix.localtime( stat.mtime );
  399. date := tm.year*200H + (tm.mon + 1)*20H + tm.mday;
  400. time := tm.hour*1000H + tm.min*40H + tm.sec;
  401. flags := {};
  402. IF IsDirectory( stat ) THEN
  403. flags := {Files.ReadOnly, Files.Directory}
  404. ELSE
  405. r := Unix.access( ADDRESSOF( fullName ), Unix.W_OK );
  406. IF r < 0 THEN flags := {Files.ReadOnly} END
  407. END;
  408. enum.PutEntry( fullName, flags, time, date, stat.size );
  409. END
  410. END;
  411. ent := Unix.readdir( dir );
  412. END;
  413. Unix.closedir( dir )
  414. END;
  415. END EnumDir;
  416. BEGIN {EXCLUSIVE}
  417. Files.SplitName( mask, prefix, fullName );
  418. Files.SplitPath( fullName, path, filemask );
  419. NEW( checkSet, "M###N" );
  420. IF path # "" THEN
  421. CleanPath( path );
  422. EnumDir( path )
  423. ELSE
  424. i := 0; j := 0;
  425. LOOP
  426. IF (searchPath[i] = " ") OR (searchPath[i] = 0X) THEN
  427. dirName[j] := 0X;
  428. EnumDir( dirName );
  429. IF searchPath[i] = 0X THEN EXIT
  430. ELSE INC( i ); j := 0
  431. END
  432. ELSE
  433. dirName[j] := searchPath[i]; INC( j ); INC( i )
  434. END
  435. END
  436. END;
  437. checkSet := NIL;
  438. END Enumerate0;
  439. END UnixFileSystem;
  440. Buffer = POINTER TO RECORD (Files.Hint)
  441. chg: BOOLEAN;
  442. org, size: LONGINT;
  443. data: ARRAY Bufsize OF CHAR;
  444. END;
  445. File* = OBJECT (Files.File)
  446. VAR
  447. fd: LONGINT;
  448. workName, registerName: Filename;
  449. tempFile: BOOLEAN;
  450. dev: Unix.DevT;
  451. ino: LONGINT;
  452. mtime: HUGEINT;
  453. fsize, fpos: SIZE;
  454. bufs: ARRAY NBufs OF Buffer;
  455. swapper, state: LONGINT;
  456. PROCEDURE & Init( fs: Files.FileSystem );
  457. BEGIN
  458. SELF.fs := fs; flags := {};
  459. END Init;
  460. PROCEDURE CreateUnixFile;
  461. CONST
  462. CreateFlags = Unix.rdwr + Unix.creat + Unix.trunc;
  463. VAR
  464. stat: Unix.Status; done: BOOLEAN; r: LONGINT;
  465. BEGIN
  466. IF state = Create THEN
  467. GetTempName( registerName, workName ); tempFile := TRUE
  468. ELSIF state = Closed THEN
  469. workName := registerName; registerName := ""; tempFile := FALSE
  470. END;
  471. r := Unix.unlink( ADDRESSOF( workName ) );
  472. (*unlink first to avoid stale NFS handles and to avoid reuse of inodes*)
  473. IF (FileTabSize - openfiles) < ResFiles THEN Kernel.GC END;
  474. fd := Unix.open( ADDRESSOF( workName ), CreateFlags, Unix.rwrwr );
  475. done := fd >= 0; r := Unix.errno();
  476. IF (~done & (r IN {Unix.ENFILE, Unix.EMFILE})) OR (done & (fd >= FileTabSize)) THEN
  477. IF done THEN r := Unix.close( fd ) END;
  478. (*GC ;*)
  479. fd := Unix.open( ADDRESSOF( workName ), CreateFlags, Unix.rwrwr );
  480. done := fd >= 0
  481. END;
  482. IF done THEN
  483. IF fd >= FileTabSize THEN
  484. r := Unix.close( fd );
  485. Halt( SELF, FALSE, "UnixFiles.File.Create: too many files open" )
  486. ELSE
  487. r := Unix.fstat( fd, stat );
  488. dev := stat.dev; ino := stat.ino; mtime := stat.mtime.sec;
  489. state := Open; fpos := 0;
  490. (*fileTab[fd].f := SELF;*)
  491. INC( openfiles );
  492. (*RegisterFinalizer( SELF, Cleanup );*)
  493. END
  494. ELSE
  495. Halt( SELF, TRUE, "UnixFiles.File.Create: open failed" );
  496. END
  497. END CreateUnixFile;
  498. PROCEDURE Flush( buf: Buffer );
  499. VAR res: LONGINT; stat: Unix.Status;
  500. BEGIN
  501. IF buf.chg THEN
  502. IF fd = NoDesc THEN CreateUnixFile END;
  503. IF buf.org # fpos THEN res := Unix.lseek( fd, buf.org, 0 ) END;
  504. res := Unix.write( fd, ADDRESSOF( buf.data ), buf.size );
  505. IF res < 0 THEN Halt( SELF, TRUE, "UnixFiles.File.Flush: write failed" ) END;
  506. fpos := buf.org + buf.size; buf.chg := FALSE;
  507. res := Unix.fstat( fd, stat ); mtime := stat.mtime.sec
  508. END
  509. END Flush;
  510. PROCEDURE Set*( VAR r: Files.Rider; pos: LONGINT );
  511. BEGIN {EXCLUSIVE}
  512. SetX( r, pos )
  513. END Set;
  514. PROCEDURE SetX( VAR r: Files.Rider; p: LONGINT );
  515. VAR org, offset, i, n, res: LONGINT; buf: Buffer;
  516. BEGIN
  517. IF p > fsize THEN p := LONGINT(fsize)
  518. ELSIF p < 0 THEN p := 0
  519. END;
  520. offset := p MOD Bufsize; org := p - offset;
  521. i := 0;
  522. WHILE (i < NBufs) & (bufs[i] # NIL) & (org # bufs[i].org) DO INC( i ) END;
  523. IF i < NBufs THEN
  524. IF bufs[i] = NIL THEN
  525. NEW( buf ); buf.chg := FALSE; buf.org := -1;
  526. bufs[i] := buf
  527. ELSE
  528. swapper := i;
  529. buf := bufs[swapper]; Flush( buf )
  530. END
  531. ELSE
  532. swapper := (swapper + 1) MOD NBufs;
  533. buf := bufs[swapper]; Flush( buf )
  534. END;
  535. IF buf.org # org THEN
  536. IF org = fsize THEN
  537. buf.size := 0
  538. ELSE
  539. IF fd = NoDesc THEN CreateUnixFile END;
  540. IF fpos # org THEN res := Unix.lseek( fd, org, 0 ) END;
  541. IF res < 0 THEN Halt( SELF, TRUE, "UnixFiles.File.Set: lseek failed" ) END;
  542. n := Unix.read( fd, ADDRESSOF( buf.data ), Bufsize );
  543. IF n < 0 THEN
  544. IF p < fsize THEN Halt( SELF, TRUE, "UnixFiles.File.Set: read failed" )
  545. ELSE n := 0
  546. END
  547. END;
  548. fpos := org + n; buf.size := n
  549. END;
  550. buf.org := org; buf.chg := FALSE
  551. ELSE
  552. org := buf.org
  553. END;
  554. r.hint := buf; r.apos := org; r.bpos := offset;
  555. r.res := 0; r.eof := FALSE;
  556. r.file := SELF; r.fs := fs
  557. END SetX;
  558. PROCEDURE Pos*( VAR r: Files.Rider ): LONGINT;
  559. BEGIN
  560. RETURN r.apos + r.bpos
  561. END Pos;
  562. PROCEDURE Read*( VAR r: Files.Rider; VAR x: CHAR );
  563. VAR offset: LONGINT; buf: Buffer;
  564. BEGIN {EXCLUSIVE}
  565. buf := r.hint(Buffer); offset := r.bpos;
  566. IF r.apos # buf.org THEN
  567. SetX( r, r.apos + offset );
  568. buf := r.hint(Buffer); offset := r.bpos
  569. END;
  570. IF (offset < buf.size) THEN
  571. x := buf.data[offset]; r.bpos := offset + 1
  572. ELSIF r.apos + offset < fsize THEN
  573. SetX( r, r.apos + offset );
  574. x := r.hint(Buffer).data[0]; r.bpos := 1
  575. ELSE
  576. x := 0X; r.eof := TRUE
  577. END
  578. END Read;
  579. PROCEDURE ReadBytes*( VAR r: Files.Rider; VAR x: ARRAY OF CHAR; ofs, len: LONGINT );
  580. VAR xpos, min, restInBuf, offset: LONGINT; buf: Buffer;
  581. BEGIN {EXCLUSIVE}
  582. x[ofs] := 0X; xpos := ofs;
  583. buf := r.hint(Buffer); offset := r.bpos;
  584. WHILE len > 0 DO
  585. IF (r.apos # buf.org) OR (offset >= Bufsize) THEN
  586. SetX( r, r.apos + offset );
  587. buf := r.hint(Buffer); offset := r.bpos
  588. END;
  589. restInBuf := buf.size - offset;
  590. IF restInBuf = 0 THEN r.res := len; r.eof := TRUE; RETURN
  591. ELSIF len > restInBuf THEN min := restInBuf
  592. ELSE min := len
  593. END;
  594. S.MOVE( ADDRESSOF( buf.data ) + offset, ADDRESSOF( x ) + xpos, min );
  595. INC( offset, min ); r.bpos := offset;
  596. INC( xpos, min ); DEC( len, min )
  597. END;
  598. r.res := 0; r.eof := FALSE;
  599. END ReadBytes;
  600. PROCEDURE Write*( VAR r: Files.Rider; x: CHAR );
  601. VAR buf: Buffer; offset: LONGINT;
  602. BEGIN {EXCLUSIVE}
  603. buf := r.hint(Buffer); offset := r.bpos;
  604. IF (r.apos # buf.org) OR (offset >= Bufsize) THEN
  605. SetX( r, r.apos + offset );
  606. buf := r.hint(Buffer); offset := r.bpos
  607. END;
  608. buf.data[offset] := x; buf.chg := TRUE;
  609. IF offset = buf.size THEN INC( buf.size ); INC( fsize ) END;
  610. r.bpos := offset + 1; r.res := Files.Ok
  611. END Write;
  612. PROCEDURE WriteBytes*( VAR r: Files.Rider; CONST x: ARRAY OF CHAR; ofs, len: LONGINT );
  613. VAR xpos, min, restInBuf, offset: LONGINT; buf: Buffer;
  614. BEGIN {EXCLUSIVE}
  615. xpos := ofs; buf := r.hint(Buffer); offset := r.bpos;
  616. WHILE len > 0 DO
  617. IF (r.apos # buf.org) OR (offset >= Bufsize) THEN
  618. SetX( r, r.apos + offset );
  619. buf := r.hint(Buffer); offset := r.bpos
  620. END;
  621. restInBuf := Bufsize - offset;
  622. IF len > restInBuf THEN min := restInBuf ELSE min := len END;
  623. S.MOVE( ADDRESSOF( x ) + xpos, ADDRESSOF( buf.data ) + offset, min );
  624. INC( offset, min ); r.bpos := offset;
  625. IF offset > buf.size THEN
  626. INC( fsize, offset - buf.size ); buf.size := offset
  627. END;
  628. INC( xpos, min ); DEC( len, min ); buf.chg := TRUE
  629. END;
  630. r.res := Files.Ok
  631. END WriteBytes;
  632. PROCEDURE Length*( ): LONGINT;
  633. BEGIN
  634. RETURN LONGINT(fsize)
  635. END Length;
  636. PROCEDURE GetDate*( VAR t, d: LONGINT );
  637. VAR stat: Unix.Status; r: LONGINT; time: Unix.TmPtr;
  638. BEGIN {EXCLUSIVE}
  639. IF fd = NoDesc THEN CreateUnixFile END;
  640. r := Unix.fstat( fd, stat );
  641. time := Unix.localtime( stat.mtime );
  642. t := time.sec + ASH( time.min, 6 ) + ASH( time.hour, 12 );
  643. d := time.mday + ASH( time.mon + 1, 5 ) + ASH( time.year, 9 );
  644. END GetDate;
  645. PROCEDURE SetDate*( t, d: LONGINT );
  646. TYPE
  647. Time = RECORD actime, modtime: LONGINT END;
  648. VAR
  649. tm: Unix.Tm; buf: Time; r: LONGINT; path: Filename;
  650. BEGIN {EXCLUSIVE}
  651. IF registerName # "" THEN COPY( registerName, path )
  652. ELSE COPY( workName, path )
  653. END;
  654. (* get year and timezone *)
  655. (* fill in new date *)
  656. tm.isdst := -1; tm.sec := t MOD 64; tm.min := t DIV 64 MOD 64;
  657. tm.hour := t DIV 4096 MOD 32;
  658. tm.mday := d MOD 32; tm.mon := d DIV 32 MOD 16 - 1; tm.year := d DIV 512;
  659. tm.wday := 0; tm.yday := 0;
  660. buf.actime := Unix.mktime( tm ); buf.modtime := buf.actime;
  661. r := Unix.utime( ADDRESSOF( path ), ADDRESSOF( buf ) );
  662. END SetDate;
  663. PROCEDURE GetAttributes*( ): SET;
  664. BEGIN {EXCLUSIVE}
  665. RETURN flags
  666. END GetAttributes;
  667. PROCEDURE SetAttributes*( attr: SET );
  668. BEGIN {EXCLUSIVE}
  669. (* flags := attr *)
  670. END SetAttributes;
  671. PROCEDURE Register0*( VAR res: LONGINT );
  672. BEGIN {EXCLUSIVE}
  673. IF (state = Create) & (registerName # "") THEN
  674. state := Closed (* shortcut renaming *)
  675. END;
  676. FlushBuffers;
  677. IF registerName # "" THEN
  678. fs.Rename0( workName, registerName, SELF, res );
  679. IF res # Files.Ok THEN
  680. Halt( SELF, FALSE, "UnixFiles.File.Register: rename failed" )
  681. END;
  682. workName := registerName; registerName := ""; tempFile := FALSE
  683. END;
  684. collection.Register(SELF);
  685. END Register0;
  686. PROCEDURE Update*;
  687. BEGIN {EXCLUSIVE}
  688. FlushBuffers
  689. END Update;
  690. PROCEDURE FlushBuffers;
  691. VAR i: LONGINT;
  692. BEGIN
  693. IF fd = NoDesc THEN CreateUnixFile END;
  694. FOR i := 0 TO NBufs - 1 DO
  695. IF bufs[i] # NIL THEN Flush( bufs[i] ) END
  696. END;
  697. END FlushBuffers;
  698. PROCEDURE Finalize*;
  699. VAR r: LONGINT;
  700. BEGIN {EXCLUSIVE}
  701. (*
  702. IF fileTab[fd].f # NIL THEN
  703. *)
  704. IF tempFile THEN r := Unix.unlink( ADDRESSOF( workName ) )
  705. ELSE FlushBuffers;
  706. END;
  707. (* fileTab[fd].f := NIL;*)
  708. r := Unix.close( fd );
  709. DEC( openfiles ); state := Closed
  710. (*
  711. END;
  712. *)
  713. END Finalize;
  714. PROCEDURE Close;
  715. BEGIN
  716. Finalize;
  717. collection.oldFiles.Remove(SELF);
  718. END Close;
  719. PROCEDURE GetName*( VAR name: ARRAY OF CHAR );
  720. BEGIN {EXCLUSIVE}
  721. IF registerName = "" THEN COPY( workName, name ) ;
  722. ELSE COPY( registerName, name )
  723. END;
  724. CleanPath( name )
  725. END GetName;
  726. END File;
  727. (*===================================================================*)
  728. (** Get the current directory. *)
  729. PROCEDURE GetWorkingDirectory*( VAR path: ARRAY OF CHAR );
  730. BEGIN
  731. COPY( cwd, path )
  732. END GetWorkingDirectory;
  733. (** Change to directory path. *)
  734. PROCEDURE ChangeDirectory*( CONST path: ARRAY OF CHAR; VAR done: BOOLEAN );
  735. VAR r: LONGINT; newdir: Filename;
  736. BEGIN
  737. IF path[0] # '/' THEN
  738. COPY( cwd, newdir ); AppendName( newdir, path );
  739. CleanPath( newdir )
  740. ELSE
  741. COPY( path, newdir );
  742. END;
  743. r := Unix.chdir( ADDRESSOF( newdir ) );
  744. IF r = 0 THEN COPY( newdir, cwd ); done := TRUE ELSE done := FALSE END
  745. END ChangeDirectory;
  746. (*===================================================================*)
  747. PROCEDURE StripPath*( CONST path: ARRAY OF CHAR; VAR name: ARRAY OF CHAR );
  748. VAR i, p: INTEGER; c: CHAR;
  749. BEGIN
  750. i := 0; p := 0;
  751. REPEAT
  752. IF path[i] = '/' THEN p := i + 1 END;
  753. INC( i )
  754. UNTIL path[i] = 0X;
  755. i := 0;
  756. REPEAT c := path[p]; name[i] := c; INC( i ); INC( p ) UNTIL c = 0X
  757. END StripPath;
  758. PROCEDURE CleanPath*( VAR path: ARRAY OF CHAR );
  759. (*
  760. /aaa/../bbb/./ccc/../ddd/. ==> /bbb/ddd
  761. ../aaa ==> CWD/../aaa ==> . . .
  762. *)
  763. VAR
  764. i, prevNameStart, nameStart: INTEGER;
  765. c1, c2, c3: CHAR;
  766. PROCEDURE prependCWD;
  767. VAR tmp: ARRAY 256 OF CHAR;
  768. BEGIN
  769. COPY( cwd, tmp ); AppendName( tmp, path ); COPY( tmp, path )
  770. END prependCWD;
  771. PROCEDURE restart;
  772. BEGIN
  773. IF path[0] = '/' THEN nameStart := 1 ELSE nameStart := 0 END;
  774. i := -1; prevNameStart := -1;
  775. END restart;
  776. PROCEDURE shift( p0, p1: INTEGER );
  777. VAR c: CHAR;
  778. BEGIN
  779. REPEAT c := path[p1]; path[p0] := c; INC( p0 ); INC( p1 ) UNTIL c = 0X;
  780. IF p0 > 1 THEN restart ELSE i := 0 END
  781. END shift;
  782. BEGIN
  783. restart;
  784. REPEAT
  785. INC( i );
  786. IF i = nameStart THEN
  787. c1 := path[i]; c2 := path[i + 1]; c3 := path[i + 2];
  788. IF c1 = '/' THEN shift( i, i + 1 ) (* // *)
  789. ELSIF c1 = '.' THEN
  790. IF c2 = 0X THEN
  791. IF i > 1 THEN DEC( i ) END;
  792. path[i] := 0X
  793. ELSIF c2 = '/' THEN shift( i, i + 2 ); (* ./ *)
  794. ELSIF (c2 = '.') & ((c3 = 0X) OR (c3 = '/')) THEN (* .. *)
  795. IF i = 0 THEN prependCWD; restart
  796. ELSIF c3 = 0X THEN DEC( i ); path[i] := 0X
  797. ELSIF c3 = '/' THEN (* ../ *)
  798. IF prevNameStart >= 0 THEN shift( prevNameStart, i + 3 ) END
  799. END
  800. END
  801. END
  802. ELSIF path[i] = '/' THEN
  803. IF i > 0 THEN prevNameStart := nameStart END;
  804. nameStart := i + 1
  805. END;
  806. UNTIL (i >= 0) & (path[i] = 0X);
  807. IF (i > 1) & (path[i - 1] = '/') THEN path[i - 1] := 0X END;
  808. IF path = "" THEN path := "." END;
  809. END CleanPath;
  810. PROCEDURE Match( CONST name, pat: ARRAY OF CHAR; i, j: INTEGER ): BOOLEAN;
  811. BEGIN
  812. IF (name[i] = 0X) & (pat[j] = 0X) THEN RETURN TRUE
  813. ELSIF pat[j] # "*" THEN RETURN (name[i] = pat[j]) & Match( name, pat, i + 1, j + 1 )
  814. ELSE (* pat[j] = "*", name[i] may be 0X *)
  815. RETURN Match( name, pat, i, j + 1 ) OR ((name[i] # 0X) & Match( name, pat, i + 1, j ))
  816. END
  817. END Match;
  818. PROCEDURE Append( VAR a: Filename; CONST this: ARRAY OF CHAR );
  819. VAR i, j: LONGINT;
  820. BEGIN
  821. i := 0; j := 0;
  822. WHILE a[i] # 0X DO INC( i ) END;
  823. WHILE (i < LEN( a ) - 1) & (this[j] # 0X) DO a[i] := this[j]; INC( i ); INC( j ) END;
  824. a[i] := 0X
  825. END Append;
  826. PROCEDURE AppendName( VAR path: Filename; CONST filename: ARRAY OF CHAR );
  827. VAR i, j, max: LONGINT;
  828. BEGIN
  829. i := 0; j := 0; max := LEN( path ) - 1;
  830. WHILE path[i] # 0X DO INC( i ) END;
  831. IF (i > 0) & (path[i - 1] # "/") THEN path[i] := "/"; INC( i ); path[i] := 0X END;
  832. Append( path, filename );
  833. END AppendName;
  834. PROCEDURE AppendInt( VAR str: Filename; n: LONGINT );
  835. VAR i: LONGINT;
  836. BEGIN
  837. i := 0;
  838. WHILE str[i] # 0X DO INC(i) END;
  839. WHILE n > 0 DO str[i] := CHR( n MOD 10 + ORD('0') ); n := n DIV 10; INC(i) END;
  840. str[i] := 0X
  841. END AppendInt;
  842. PROCEDURE IsFullName( CONST name: ARRAY OF CHAR ): BOOLEAN;
  843. VAR i: INTEGER; ch: CHAR;
  844. BEGIN
  845. i := 0; ch := name[0];
  846. WHILE (ch # 0X) & (ch # "/") DO INC( i ); ch := name[i] END;
  847. RETURN ch = "/"
  848. END IsFullName;
  849. PROCEDURE Halt( f: File; unixError: BOOLEAN; CONST msg: ARRAY OF CHAR );
  850. VAR fd, errno: LONGINT;
  851. workName, registerName: Filename;
  852. BEGIN
  853. IF f = NIL THEN
  854. workName := "???"; registerName := "???"
  855. ELSE
  856. workName := f.workName; registerName := f.registerName; fd := f.fd
  857. END;
  858. IF unixError THEN errno := Unix.errno( ); Unix.Perror( msg ) END;
  859. HALT( 99 )
  860. END Halt;
  861. (*
  862. PROCEDURE RegisterFinalizer( obj: ANY; fin: Heaps.Finalizer );
  863. VAR n: Heaps.FinalizerNode;
  864. BEGIN
  865. NEW( n ); n.finalizer := fin; Heaps.AddFinalizer( obj, n );
  866. END RegisterFinalizer;
  867. *)
  868. (*
  869. PROCEDURE GC;
  870. BEGIN
  871. Kernel.GC;
  872. AwaitFinalizingDone
  873. END GC;
  874. PROCEDURE AwaitFinalizingDone;
  875. BEGIN
  876. (* wait until finalizers have finished! (Cleanup)*)
  877. WHILE Machine.GCacquired DO Objects.Sleep( 10 ) END
  878. END AwaitFinalizingDone;
  879. *)
  880. PROCEDURE ResetBuffers( f: File; VAR stat: Unix.Status );
  881. VAR i: INTEGER;
  882. BEGIN
  883. f.fsize := stat.size;
  884. IF (f.mtime # stat.mtime.sec) THEN
  885. FOR i := 0 TO NBufs - 1 DO
  886. IF f.bufs[i] # NIL THEN f.bufs[i].org := -1; f.bufs[i] := NIL END;
  887. END;
  888. f.swapper := -1; f.mtime := stat.mtime.sec
  889. END
  890. END ResetBuffers;
  891. (*
  892. PROCEDURE FindCachedEntry( VAR stat: Unix.Status ): File;
  893. VAR f: File; i: INTEGER;
  894. BEGIN
  895. FOR i := 0 TO FileTabSize - 1 DO
  896. f := fileTab[i].f;
  897. IF (f # NIL ) & (stat.ino = f.ino) & (stat.dev = f.dev) THEN
  898. (* possible different name but same file! *)
  899. ResetBuffers( f, stat );
  900. RETURN f
  901. END;
  902. END;
  903. RETURN NIL
  904. END FindCachedEntry;
  905. *)
  906. PROCEDURE MakePath( CONST dir, name: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR );
  907. VAR i, j: INTEGER;
  908. BEGIN
  909. i := 0; j := 0;
  910. WHILE dir[i] # 0X DO dest[i] := dir[i]; INC( i ) END;
  911. IF (i>0) & (dest[i - 1] # "/") THEN dest[i] := "/"; INC( i ) END;
  912. WHILE name[j] # 0X DO dest[i] := name[j]; INC( i ); INC( j ) END;
  913. dest[i] := 0X
  914. END MakePath;
  915. PROCEDURE ScanPath( VAR pos: LONGINT; VAR dir: ARRAY OF CHAR );
  916. VAR i: LONGINT; ch: CHAR;
  917. BEGIN
  918. i := 0; ch := searchPath[pos];
  919. WHILE ch = " " DO INC( pos ); ch := searchPath[pos] END;
  920. WHILE ch > " " DO dir[i] := ch; INC( i ); INC( pos ); ch := searchPath[pos] END;
  921. dir[i] := 0X
  922. END ScanPath;
  923. PROCEDURE GetTempName( CONST finalName: ARRAY OF CHAR; VAR tempName: Filename );
  924. VAR n, i, j, pe, pid: LONGINT;
  925. BEGIN
  926. INC(tempno); n := tempno; i := 0; j := 0; pe := 1;
  927. WHILE finalName[j] = ' ' DO INC(j) END; (* skip leading spaces *)
  928. IF finalName[j] # "/" THEN (* relative pathname *)
  929. WHILE cwd[i] # 0X DO tempName[i] := cwd[i]; INC(i) END;
  930. IF tempName[i - 1] # '/' THEN tempName[i] := '/'; INC(i) END;
  931. pe := i - 1
  932. END;
  933. WHILE finalName[j] # 0X DO tempName[i] := finalName[j]; INC(i); INC(j) END;
  934. WHILE (i > pe) & (tempName[i-1] # '/') DO DEC(i) END; (* remove filename *)
  935. tempName[i] := 0X;
  936. Append( tempName, ".tmp." );
  937. AppendInt( tempName, n ); Append( tempName, "." );
  938. pid := Unix.getpid();
  939. AppendInt( tempName, pid )
  940. END GetTempName;
  941. PROCEDURE Cleanup( obj: ANY );
  942. VAR f: File;
  943. BEGIN
  944. f := S.VAL( File, obj ); f.Close
  945. END Cleanup;
  946. (*
  947. PROCEDURE CloseFiles;
  948. VAR i: LONGINT; f: File;
  949. BEGIN
  950. i := 0;
  951. WHILE i < FileTabSize DO
  952. f := fileTab[i].f;
  953. IF f # NIL THEN f.Close END;
  954. INC( i )
  955. END;
  956. END CloseFiles;
  957. *)
  958. PROCEDURE Install;
  959. VAR aliasFS: AliasFileSystem;
  960. BEGIN
  961. NEW(collection);
  962. NEW( unixFS ); (* Files.Add( unixFS, "" ); *)
  963. NEW( aliasFS, unixFS ); Files.Add( aliasFS, "searcher" )
  964. END Install;
  965. PROCEDURE Initialize;
  966. VAR a: ADDRESS; i: INTEGER; ch: CHAR;
  967. BEGIN
  968. (* get current working directory *)
  969. a := Unix.getenv( ADDRESSOF( "PWD" ) );
  970. IF a > 0 THEN
  971. i := 0;
  972. REPEAT S.GET( a, ch ); INC( a ); cwd[i] := ch; INC( i ) UNTIL ch = 0X;
  973. ELSE
  974. (* $PWD not set *)
  975. a := Unix.getcwd( ADDRESSOF( cwd ), LEN( cwd ) )
  976. END;
  977. i := 0;
  978. WHILE cwd[i] # 0X DO INC( i ) END;
  979. DEC( i );
  980. IF (i > 0) & (cwd[i] = '/') THEN cwd[i] := 0X END;
  981. (* get search pathes *)
  982. a := Unix.getenv( ADDRESSOF( "AOSPATH" ) ); i := 0;
  983. IF a = 0 THEN
  984. Log.String( "UnixFiles.Initialize: environment variable AOSPATH not defined" ); Log.Ln;
  985. (* Unix.exit( 1 ) *)
  986. ELSE
  987. REPEAT
  988. S.GET( a, ch ); INC( a );
  989. IF ch = ":" THEN ch := " " END;
  990. searchPath[i] := ch; INC( i )
  991. UNTIL ch = 0X;
  992. END;
  993. i := 0;
  994. (*
  995. WHILE i < FileTabSize DO fileTab[i].f := NIL; INC( i ) END;
  996. *)
  997. tempno := 1; openfiles := 0;
  998. Modules.InstallTermHandler( Finalization )
  999. END Initialize;
  1000. PROCEDURE Finalization;
  1001. VAR ft: Files.FileSystemTable; i: LONGINT;
  1002. BEGIN
  1003. Files.GetList( ft );
  1004. IF ft # NIL THEN
  1005. FOR i := 0 TO LEN( ft^ ) - 1 DO
  1006. IF ft[i] IS AliasFileSystem THEN Files.Remove( ft[i] ) END
  1007. END
  1008. END;
  1009. unixFS.Finalize;
  1010. END Finalization;
  1011. BEGIN
  1012. Initialize;
  1013. Install
  1014. END UnixFiles.