Unix.UnixFiles.Mod 31 KB

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