Unix.UnixFiles.Mod 29 KB

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