2
0

Unix.UnixFiles.Mod 35 KB

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