Generic.Unix.UnixFiles.Mod 35 KB

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