2
0

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