Unix.UnixFiles.Mod 34 KB

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