FIO.mod 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761
  1. (* FIO.mod provides a simple buffered file input/output library.
  2. Copyright (C) 2001-2025 Free Software Foundation, Inc.
  3. Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
  4. This file is part of GNU Modula-2.
  5. GNU Modula-2 is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 3, or (at your option)
  8. any later version.
  9. GNU Modula-2 is distributed in the hope that it will be useful, but
  10. WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. General Public License for more details.
  13. Under Section 7 of GPL version 3, you are granted additional
  14. permissions described in the GCC Runtime Library Exception, version
  15. 3.1, as published by the Free Software Foundation.
  16. You should have received a copy of the GNU General Public License and
  17. a copy of the GCC Runtime Library Exception along with this program;
  18. see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
  19. <http://www.gnu.org/licenses/>. *)
  20. IMPLEMENTATION MODULE FIO ;
  21. (*
  22. Title : FIO
  23. Author : Gaius Mulley
  24. System : UNIX (gm2)
  25. Date : Thu Sep 2 22:07:21 1999
  26. Last edit : Thu Sep 2 22:07:21 1999
  27. Description: a complete reimplememtation of FIO.mod
  28. provides a simple buffered file input/output library.
  29. *)
  30. IMPORT Strings;
  31. FROM SYSTEM IMPORT ADR, TSIZE, WORD, COFF_T ;
  32. FROM ASCII IMPORT nl, nul, tab ;
  33. FROM StrLib IMPORT StrLen, StrConCat, StrCopy ;
  34. FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
  35. FROM NumberIO IMPORT CardToStr ;
  36. FROM Indexing IMPORT Index, InitIndex, InBounds, HighIndice, LowIndice, PutIndice, GetIndice ;
  37. FROM M2RTS IMPORT InstallTerminationProcedure ;
  38. FROM libc IMPORT exit, open, creat, read, write, close, lseek, strncpy, memcpy, unlink ;
  39. FROM wrapc IMPORT SeekSet, SeekEnd, ReadOnly, WriteOnly ;
  40. CONST
  41. MaxBufferLength = 1024*16 ;
  42. MaxErrorString = 1024* 8 ;
  43. CreatePermissions = 666B;
  44. TYPE
  45. FileUsage = (unused, openedforread, openedforwrite, openedforrandom) ;
  46. FileStatus = (successful, outofmemory, toomanyfilesopen, failed, connectionfailure, endofline, endoffile) ;
  47. NameInfo = RECORD
  48. address: ADDRESS ;
  49. size : CARDINAL ;
  50. END ;
  51. Buffer = POINTER TO buf ;
  52. buf = RECORD
  53. valid : BOOLEAN ; (* are the field valid? *)
  54. bufstart: LONGINT ; (* the position of buffer in file *)
  55. position: CARDINAL ; (* where are we through this buffer *)
  56. address : ADDRESS ; (* dynamic buffer address *)
  57. filled : CARDINAL ; (* length of the buffer filled *)
  58. size : CARDINAL ; (* maximum space in this buffer *)
  59. left : CARDINAL ; (* number of bytes left to read *)
  60. contents: POINTER TO ARRAY [0..MaxBufferLength] OF CHAR ;
  61. END ;
  62. FileDescriptor = POINTER TO fds ;
  63. fds = RECORD
  64. unixfd: INTEGER ;
  65. name : NameInfo ;
  66. state : FileStatus ;
  67. usage : FileUsage ;
  68. output: BOOLEAN ; (* is this file going to write data *)
  69. buffer: Buffer ;
  70. abspos: LONGINT ; (* absolute position into file. *)
  71. END ; (* reflects low level reads which *)
  72. (* means this value will normally *)
  73. (* be further through the file than *)
  74. (* bufstart above. *)
  75. PtrToChar = POINTER TO CHAR ;
  76. VAR
  77. FileInfo: Index ;
  78. Error : File ; (* not stderr, this is an unused file handle
  79. which only serves to hold status values
  80. when we cannot create a new file handle *)
  81. (*
  82. GetUnixFileDescriptor - returns the UNIX file descriptor of a file.
  83. *)
  84. PROCEDURE GetUnixFileDescriptor (f: File) : INTEGER ;
  85. VAR
  86. fd: FileDescriptor ;
  87. BEGIN
  88. IF f#Error
  89. THEN
  90. fd := GetIndice(FileInfo, f) ;
  91. IF fd#NIL
  92. THEN
  93. RETURN( fd^.unixfd )
  94. END
  95. END ;
  96. FormatError1('file %d has not been opened or is out of range\n', f) ;
  97. RETURN( -1 )
  98. END GetUnixFileDescriptor ;
  99. (*
  100. WriteString - writes a string to file, f.
  101. *)
  102. PROCEDURE WriteString (f: File; a: ARRAY OF CHAR) ;
  103. VAR
  104. l: CARDINAL ;
  105. BEGIN
  106. l := StrLen(a) ;
  107. IF WriteNBytes(f, l, ADR(a))#l
  108. THEN
  109. END
  110. END WriteString ;
  111. (*
  112. Max - returns the maximum of two values.
  113. *)
  114. PROCEDURE Max (a, b: CARDINAL) : CARDINAL ;
  115. BEGIN
  116. IF a>b
  117. THEN
  118. RETURN( a )
  119. ELSE
  120. RETURN( b )
  121. END
  122. END Max ;
  123. (*
  124. Min - returns the minimum of two values.
  125. *)
  126. PROCEDURE Min (a, b: CARDINAL) : CARDINAL ;
  127. BEGIN
  128. IF a<b
  129. THEN
  130. RETURN( a )
  131. ELSE
  132. RETURN( b )
  133. END
  134. END Min ;
  135. (*
  136. GetNextFreeDescriptor - returns the index to the FileInfo array indicating
  137. the next free slot.
  138. *)
  139. PROCEDURE GetNextFreeDescriptor () : File ;
  140. VAR
  141. f, h: File ;
  142. fd : FileDescriptor ;
  143. BEGIN
  144. f := Error+1 ;
  145. h := HighIndice(FileInfo) ;
  146. LOOP
  147. IF f<=h
  148. THEN
  149. fd := GetIndice(FileInfo, f) ;
  150. IF fd=NIL
  151. THEN
  152. RETURN( f )
  153. END
  154. END ;
  155. INC(f) ;
  156. IF f>h
  157. THEN
  158. PutIndice(FileInfo, f, NIL) ; (* create new slot *)
  159. RETURN( f )
  160. END
  161. END
  162. END GetNextFreeDescriptor ;
  163. (*
  164. IsNoError - returns a TRUE if no error has occured on file, f.
  165. *)
  166. PROCEDURE IsNoError (f: File) : BOOLEAN ;
  167. VAR
  168. fd: FileDescriptor ;
  169. BEGIN
  170. IF f=Error
  171. THEN
  172. RETURN( FALSE )
  173. ELSE
  174. fd := GetIndice(FileInfo, f) ;
  175. RETURN( (fd#NIL) AND ((fd^.state=successful) OR (fd^.state=endoffile) OR (fd^.state=endofline)) )
  176. END
  177. END IsNoError ;
  178. (*
  179. IsActive - returns TRUE if the file, f, is still active.
  180. *)
  181. PROCEDURE IsActive (f: File) : BOOLEAN ;
  182. BEGIN
  183. IF f=Error
  184. THEN
  185. RETURN( FALSE )
  186. ELSE
  187. RETURN( GetIndice(FileInfo, f)#NIL )
  188. END
  189. END IsActive ;
  190. (*
  191. openToRead - attempts to open a file, fname, for reading and
  192. it returns this file.
  193. The success of this operation can be checked by
  194. calling IsNoError.
  195. *)
  196. PROCEDURE openToRead (fname: ADDRESS; flength: CARDINAL) : File ;
  197. VAR
  198. f: File ;
  199. BEGIN
  200. f := GetNextFreeDescriptor() ;
  201. IF f=Error
  202. THEN
  203. SetState(f, toomanyfilesopen)
  204. ELSE
  205. f := InitializeFile(f, fname, flength, successful, openedforread, FALSE, MaxBufferLength) ;
  206. ConnectToUnix(f, FALSE, FALSE)
  207. END ;
  208. RETURN( f )
  209. END openToRead ;
  210. (*
  211. openToWrite - attempts to open a file, fname, for write and
  212. it returns this file.
  213. The success of this operation can be checked by
  214. calling IsNoError.
  215. *)
  216. PROCEDURE openToWrite (fname: ADDRESS; flength: CARDINAL) : File ;
  217. VAR
  218. f: File ;
  219. BEGIN
  220. f := GetNextFreeDescriptor() ;
  221. IF f=Error
  222. THEN
  223. SetState(f, toomanyfilesopen)
  224. ELSE
  225. f := InitializeFile(f, fname, flength, successful, openedforwrite, TRUE, MaxBufferLength) ;
  226. ConnectToUnix(f, TRUE, TRUE)
  227. END ;
  228. RETURN( f )
  229. END openToWrite ;
  230. (*
  231. openForRandom - attempts to open a file, fname, for random access
  232. read or write and it returns this file.
  233. The success of this operation can be checked by
  234. calling IsNoError.
  235. towrite, determines whether the file should be
  236. opened for writing or reading.
  237. *)
  238. PROCEDURE openForRandom (fname: ADDRESS; flength: CARDINAL;
  239. towrite, newfile: BOOLEAN) : File ;
  240. VAR
  241. f: File ;
  242. BEGIN
  243. f := GetNextFreeDescriptor() ;
  244. IF f=Error
  245. THEN
  246. SetState(f, toomanyfilesopen)
  247. ELSE
  248. f := InitializeFile(f, fname, flength, successful, openedforrandom, towrite, MaxBufferLength) ;
  249. ConnectToUnix(f, towrite, newfile)
  250. END ;
  251. RETURN( f )
  252. END openForRandom ;
  253. (*
  254. exists - returns TRUE if a file named, fname exists for reading.
  255. *)
  256. PROCEDURE exists (fname: ADDRESS; flength: CARDINAL) : BOOLEAN ;
  257. VAR
  258. f: File ;
  259. BEGIN
  260. f := openToRead(fname, flength) ;
  261. IF IsNoError(f)
  262. THEN
  263. Close(f) ;
  264. RETURN( TRUE )
  265. ELSE
  266. Close(f) ;
  267. RETURN( FALSE )
  268. END
  269. END exists ;
  270. (*
  271. SetState - sets the field, state, of file, f, to, s.
  272. *)
  273. PROCEDURE SetState (f: File; s: FileStatus) ;
  274. VAR
  275. fd: FileDescriptor ;
  276. BEGIN
  277. fd := GetIndice(FileInfo, f) ;
  278. fd^.state := s
  279. END SetState ;
  280. (*
  281. InitializeFile - initialize a file descriptor
  282. *)
  283. PROCEDURE InitializeFile (f: File; fname: ADDRESS;
  284. flength: CARDINAL; fstate: FileStatus;
  285. use: FileUsage;
  286. towrite: BOOLEAN; buflength: CARDINAL) : File ;
  287. VAR
  288. p : PtrToChar ;
  289. fd: FileDescriptor ;
  290. BEGIN
  291. NEW(fd) ;
  292. IF fd=NIL
  293. THEN
  294. SetState(Error, outofmemory) ;
  295. RETURN( Error )
  296. ELSE
  297. PutIndice(FileInfo, f, fd) ;
  298. WITH fd^ DO
  299. name.size := flength+1 ; (* need to guarantee the nul for C *)
  300. usage := use ;
  301. output := towrite ;
  302. ALLOCATE(name.address, name.size) ;
  303. IF name.address=NIL
  304. THEN
  305. state := outofmemory ;
  306. RETURN( f )
  307. END ;
  308. name.address := strncpy(name.address, fname, flength) ;
  309. (* and assign nul to the last byte *)
  310. p := name.address ;
  311. INC(p, flength) ;
  312. p^ := nul ;
  313. abspos := 0 ;
  314. (* now for the buffer *)
  315. NEW(buffer) ;
  316. IF buffer=NIL
  317. THEN
  318. SetState(Error, outofmemory) ;
  319. RETURN( Error )
  320. ELSE
  321. WITH buffer^ DO
  322. valid := FALSE ;
  323. bufstart := 0 ;
  324. size := buflength ;
  325. position := 0 ;
  326. filled := 0 ;
  327. IF size=0
  328. THEN
  329. address := NIL
  330. ELSE
  331. ALLOCATE(address, size) ;
  332. IF address=NIL
  333. THEN
  334. state := outofmemory ;
  335. RETURN( f )
  336. END
  337. END ;
  338. IF towrite
  339. THEN
  340. left := size
  341. ELSE
  342. left := 0
  343. END ;
  344. contents := address ; (* provides easy access for reading characters *)
  345. END ;
  346. state := fstate
  347. END
  348. END
  349. END ;
  350. RETURN( f )
  351. END InitializeFile ;
  352. (*
  353. ConnectToUnix - connects a FIO file to a UNIX file descriptor.
  354. *)
  355. PROCEDURE ConnectToUnix (f: File; towrite, newfile: BOOLEAN) ;
  356. VAR
  357. fd: FileDescriptor ;
  358. BEGIN
  359. IF f#Error
  360. THEN
  361. fd := GetIndice(FileInfo, f) ;
  362. IF fd#NIL
  363. THEN
  364. WITH fd^ DO
  365. IF towrite
  366. THEN
  367. IF newfile
  368. THEN
  369. unixfd := creat(name.address, CreatePermissions)
  370. ELSE
  371. unixfd := open(name.address, INTEGER (WriteOnly ()), 0)
  372. END
  373. ELSE
  374. unixfd := open(name.address, INTEGER (ReadOnly ()), 0)
  375. END ;
  376. IF unixfd<0
  377. THEN
  378. state := connectionfailure
  379. END
  380. END
  381. END
  382. END
  383. END ConnectToUnix ;
  384. (*
  385. The following functions are wrappers for the above.
  386. *)
  387. PROCEDURE Exists (fname: ARRAY OF CHAR) : BOOLEAN ;
  388. BEGIN
  389. RETURN( exists(ADR(fname), StrLen(fname)) )
  390. END Exists ;
  391. PROCEDURE OpenToRead (fname: ARRAY OF CHAR) : File ;
  392. BEGIN
  393. RETURN( openToRead(ADR(fname), StrLen(fname)) )
  394. END OpenToRead ;
  395. PROCEDURE OpenToWrite (fname: ARRAY OF CHAR) : File ;
  396. BEGIN
  397. RETURN( openToWrite(ADR(fname), StrLen(fname)) )
  398. END OpenToWrite ;
  399. PROCEDURE OpenForRandom (fname: ARRAY OF CHAR;
  400. towrite: BOOLEAN; newfile: BOOLEAN) : File ;
  401. BEGIN
  402. RETURN( openForRandom(ADR(fname), StrLen(fname), towrite, newfile) )
  403. END OpenForRandom ;
  404. (*
  405. Close - close a file which has been previously opened using:
  406. OpenToRead, OpenToWrite, OpenForRandom.
  407. It is correct to close a file which has an error status.
  408. *)
  409. PROCEDURE Close (f: File) ;
  410. VAR
  411. fd: FileDescriptor ;
  412. BEGIN
  413. IF f#Error
  414. THEN
  415. fd := GetIndice(FileInfo, f) ;
  416. (*
  417. we allow users to close files which have an error status
  418. *)
  419. IF fd#NIL
  420. THEN
  421. FlushBuffer(f) ;
  422. WITH fd^ DO
  423. IF unixfd>=0
  424. THEN
  425. IF close(unixfd)#0
  426. THEN
  427. FormatError1('failed to close file (%s)\n', name.address) ;
  428. state := failed (* --fixme-- too late to notify user (unless we return a BOOLEAN) *)
  429. END
  430. END ;
  431. IF name.address#NIL
  432. THEN
  433. DEALLOCATE(name.address, name.size)
  434. END ;
  435. IF buffer#NIL
  436. THEN
  437. WITH buffer^ DO
  438. IF address#NIL
  439. THEN
  440. DEALLOCATE(address, size)
  441. END
  442. END ;
  443. DISPOSE(buffer) ;
  444. buffer := NIL
  445. END
  446. END ;
  447. DISPOSE(fd) ;
  448. PutIndice(FileInfo, f, NIL)
  449. END
  450. END
  451. END Close ;
  452. (*
  453. Unlink - Delete a file which has been opened using
  454. OpenToRead, OpenToWrite, OpenForRandom.
  455. *)
  456. PROCEDURE Unlink ( f : File );
  457. VAR
  458. fname: ARRAY[0..256] OF CHAR ;
  459. BEGIN
  460. GetFileName(f,fname);
  461. fd := GetIndice(FileInfo, f) ;
  462. Close(f);
  463. unlink(ADR(fname));
  464. END Unlink;
  465. (*
  466. Delete - Delete a file which has been opened using
  467. OpenToRead, OpenToWrite, OpenForRandom.
  468. *)
  469. PROCEDURE Delete (fname: ARRAY OF CHAR ) ;
  470. VAR
  471. fd: FileDescriptor ;
  472. BEGIN
  473. fd := GetFDesc(fname);
  474. IF fd # NIL THEN
  475. Close(fd);
  476. (*Unlink(ADR(fname));*)
  477. END;
  478. END Delete;
  479. (*
  480. ReadFromBuffer - attempts to read, nBytes, from file, f.
  481. It firstly consumes the buffer and then performs
  482. direct unbuffered reads. This should only be used
  483. when wishing to read large files.
  484. The actual number of bytes read is returned.
  485. -1 is returned if EOF is reached.
  486. *)
  487. PROCEDURE ReadFromBuffer (f: File; a: ADDRESS; nBytes: CARDINAL) : INTEGER ;
  488. VAR
  489. t : ADDRESS ;
  490. result: INTEGER ;
  491. total,
  492. n : CARDINAL ;
  493. p : POINTER TO BYTE ;
  494. fd : FileDescriptor ;
  495. BEGIN
  496. IF f#Error
  497. THEN
  498. total := 0 ; (* how many bytes have we read *)
  499. fd := GetIndice(FileInfo, f) ;
  500. WITH fd^ DO
  501. (* extract from the buffer first *)
  502. IF (buffer#NIL) AND (buffer^.valid)
  503. THEN
  504. WITH buffer^ DO
  505. IF left>0
  506. THEN
  507. IF nBytes=1
  508. THEN
  509. (* too expensive to call memcpy for 1 character *)
  510. p := a ;
  511. p^ := contents^[position] ;
  512. DEC(left) ; (* remove consumed bytes *)
  513. INC(position) ; (* move onwards n bytes *)
  514. nBytes := 0 ; (* reduce the amount for future direct *)
  515. (* read *)
  516. RETURN( 1 )
  517. ELSE
  518. n := Min(left, nBytes) ;
  519. t := address ;
  520. INC(t, position) ;
  521. p := memcpy(a, t, n) ;
  522. DEC(left, n) ; (* remove consumed bytes *)
  523. INC(position, n) ; (* move onwards n bytes *)
  524. (* move onwards ready for direct reads *)
  525. INC(a, n) ;
  526. DEC(nBytes, n) ; (* reduce the amount for future direct *)
  527. (* read *)
  528. INC(total, n) ;
  529. RETURN( total ) (* much cleaner to return now, *)
  530. END (* difficult to record an error if *)
  531. END (* the read below returns -1 *)
  532. END
  533. END ;
  534. IF nBytes>0
  535. THEN
  536. (* still more to read *)
  537. result := read(unixfd, a, INTEGER(nBytes)) ;
  538. IF result>0
  539. THEN
  540. INC(total, result) ;
  541. INC(abspos, result) ;
  542. (* now disable the buffer as we read directly into, a. *)
  543. IF buffer#NIL
  544. THEN
  545. buffer^.valid := FALSE
  546. END ;
  547. ELSE
  548. IF result=0
  549. THEN
  550. (* eof reached *)
  551. state := endoffile
  552. ELSE
  553. state := failed
  554. END ;
  555. (* indicate buffer is empty *)
  556. IF buffer#NIL
  557. THEN
  558. WITH buffer^ DO
  559. valid := FALSE ;
  560. left := 0 ;
  561. position := 0 ;
  562. IF address#NIL
  563. THEN
  564. contents^[position] := nul
  565. END
  566. END
  567. END ;
  568. RETURN( -1 )
  569. END
  570. END
  571. END ;
  572. RETURN( total )
  573. ELSE
  574. RETURN( -1 )
  575. END
  576. END ReadFromBuffer ;
  577. (*
  578. ReadNBytes - reads nBytes of a file into memory area, dest, returning
  579. the number of bytes actually read.
  580. This function will consume from the buffer and then
  581. perform direct libc reads. It is ideal for large reads.
  582. *)
  583. PROCEDURE ReadNBytes (f: File; nBytes: CARDINAL; dest: ADDRESS) : CARDINAL ;
  584. VAR
  585. n: INTEGER ;
  586. p: POINTER TO CHAR ;
  587. BEGIN
  588. IF f # Error
  589. THEN
  590. CheckAccess (f, openedforread, FALSE) ;
  591. n := ReadFromBuffer (f, dest, nBytes) ;
  592. IF n <= 0
  593. THEN
  594. RETURN 0
  595. ELSE
  596. p := dest ;
  597. INC (p, n-1) ;
  598. SetEndOfLine (f, p^) ;
  599. RETURN n
  600. END
  601. ELSE
  602. RETURN 0
  603. END
  604. END ReadNBytes ;
  605. (*
  606. BufferedRead - will read, nBytes, through the buffer.
  607. Similar to ReadFromBuffer, but this function will always
  608. read into the buffer before copying into memory.
  609. Useful when performing small reads.
  610. *)
  611. PROCEDURE BufferedRead (f: File; nBytes: CARDINAL; dest: ADDRESS) : INTEGER ;
  612. VAR
  613. src : ADDRESS ;
  614. total,
  615. n : INTEGER ;
  616. p : POINTER TO BYTE ;
  617. fd : FileDescriptor ;
  618. BEGIN
  619. IF f#Error
  620. THEN
  621. fd := GetIndice (FileInfo, f) ;
  622. total := 0 ; (* how many bytes have we read *)
  623. IF fd#NIL
  624. THEN
  625. WITH fd^ DO
  626. (* extract from the buffer first *)
  627. IF buffer # NIL
  628. THEN
  629. WITH buffer^ DO
  630. WHILE nBytes > 0 DO
  631. IF (left > 0) AND valid
  632. THEN
  633. IF nBytes = 1
  634. THEN
  635. (* too expensive to call memcpy for 1 character *)
  636. p := dest ;
  637. p^ := contents^[position] ;
  638. DEC (left) ; (* remove consumed byte *)
  639. INC (position) ; (* move onwards n byte *)
  640. INC (total) ;
  641. RETURN( total )
  642. ELSE
  643. n := Min (left, nBytes) ;
  644. src := address ;
  645. INC (src, position) ;
  646. p := memcpy (dest, src, n) ;
  647. DEC (left, n) ; (* remove consumed bytes *)
  648. INC (position, n) ; (* move onwards n bytes *)
  649. (* move onwards ready for direct reads *)
  650. INC (dest, n) ;
  651. DEC (nBytes, n) ; (* reduce the amount for future direct *)
  652. (* read *)
  653. INC (total, n)
  654. END
  655. ELSE
  656. (* refill buffer *)
  657. n := read (unixfd, address, size) ;
  658. IF n >= 0
  659. THEN
  660. valid := TRUE ;
  661. position := 0 ;
  662. left := n ;
  663. filled := n ;
  664. bufstart := abspos ;
  665. INC (abspos, n) ;
  666. IF n = 0
  667. THEN
  668. (* eof reached *)
  669. state := endoffile ;
  670. RETURN( -1 )
  671. END
  672. ELSE
  673. valid := FALSE ;
  674. position := 0 ;
  675. left := 0 ;
  676. filled := 0 ;
  677. state := failed ;
  678. RETURN( total )
  679. END
  680. END
  681. END
  682. END ;
  683. RETURN( total )
  684. END
  685. END
  686. END
  687. END ;
  688. RETURN( -1 )
  689. END BufferedRead ;
  690. (*
  691. HandleEscape - translates \n and \t into their respective ascii codes.
  692. *)
  693. PROCEDURE HandleEscape (VAR dest: ARRAY OF CHAR; src: ARRAY OF CHAR;
  694. VAR i, j: CARDINAL; HighSrc, HighDest: CARDINAL) ;
  695. BEGIN
  696. IF (i+1<HighSrc) AND (src[i]='\') AND (j<HighDest)
  697. THEN
  698. IF src[i+1]='n'
  699. THEN
  700. (* requires a newline *)
  701. dest[j] := nl ;
  702. INC(j) ;
  703. INC(i, 2)
  704. ELSIF src[i+1]='t'
  705. THEN
  706. (* requires a tab (yuck) tempted to fake this but I better not.. *)
  707. dest[j] := tab ;
  708. INC(j) ;
  709. INC(i, 2)
  710. ELSE
  711. (* copy escaped character *)
  712. INC(i) ;
  713. dest[j] := src[i] ;
  714. INC(j) ;
  715. INC(i)
  716. END
  717. END
  718. END HandleEscape ;
  719. (*
  720. Cast - casts a := b
  721. *)
  722. PROCEDURE Cast (VAR a: ARRAY OF BYTE; b: ARRAY OF BYTE) ;
  723. VAR
  724. i: CARDINAL ;
  725. BEGIN
  726. IF HIGH(a)=HIGH(b)
  727. THEN
  728. FOR i := 0 TO HIGH(a) DO
  729. a[i] := b[i]
  730. END
  731. ELSE
  732. FormatError('cast failed')
  733. END
  734. END Cast ;
  735. (*
  736. StringFormat1 - converts string, src, into, dest, together with encapsulated
  737. entity, w. It only formats the first %s or %d with n.
  738. *)
  739. PROCEDURE StringFormat1 (VAR dest: ARRAY OF CHAR; src: ARRAY OF CHAR;
  740. w: ARRAY OF BYTE) ;
  741. VAR
  742. HighSrc,
  743. HighDest,
  744. c, i, j : CARDINAL ;
  745. str : ARRAY [0..MaxErrorString] OF CHAR ;
  746. p : POINTER TO CHAR ;
  747. BEGIN
  748. HighSrc := StrLen(src) ;
  749. HighDest := HIGH(dest) ;
  750. p := NIL ;
  751. c := 0 ;
  752. i := 0 ;
  753. j := 0 ;
  754. WHILE (i<HighSrc) AND (src[i]#nul) AND (j<HighDest) AND (src[i]#'%') DO
  755. IF src[i]='\'
  756. THEN
  757. HandleEscape(dest, src, i, j, HighSrc, HighDest)
  758. ELSE
  759. dest[j] := src[i] ;
  760. INC(i) ;
  761. INC(j)
  762. END
  763. END ;
  764. IF (i+1<HighSrc) AND (src[i]='%') AND (j<HighDest)
  765. THEN
  766. IF src[i+1]='s'
  767. THEN
  768. Cast(p, w) ;
  769. WHILE (j<HighDest) AND (p^#nul) DO
  770. dest[j] := p^ ;
  771. INC(j) ;
  772. INC(p)
  773. END ;
  774. IF j<HighDest
  775. THEN
  776. dest[j] := nul
  777. END ;
  778. j := StrLen(dest) ;
  779. INC(i, 2)
  780. ELSIF src[i+1]='d'
  781. THEN
  782. dest[j] := nul ;
  783. Cast(c, w) ;
  784. CardToStr(c, 0, str) ;
  785. StrConCat(dest, str, dest) ;
  786. j := StrLen(dest) ;
  787. INC(i, 2)
  788. ELSE
  789. dest[j] := src[i] ;
  790. INC(i) ;
  791. INC(j)
  792. END
  793. END ;
  794. (* and finish off copying src into dest *)
  795. WHILE (i<HighSrc) AND (src[i]#nul) AND (j<HighDest) DO
  796. IF src[i]='\'
  797. THEN
  798. HandleEscape(dest, src, i, j, HighSrc, HighDest)
  799. ELSE
  800. dest[j] := src[i] ;
  801. INC(i) ;
  802. INC(j)
  803. END
  804. END ;
  805. IF j<HighDest
  806. THEN
  807. dest[j] := nul
  808. END ;
  809. END StringFormat1 ;
  810. (*
  811. FormatError - provides a orthoganal counterpart to the procedure below.
  812. *)
  813. PROCEDURE FormatError (a: ARRAY OF CHAR) ;
  814. BEGIN
  815. WriteString (StdErr, a)
  816. END FormatError ;
  817. (*
  818. FormatError1 - generic error procedure taking standard format string
  819. and single parameter.
  820. *)
  821. PROCEDURE FormatError1 (a: ARRAY OF CHAR; w: ARRAY OF BYTE) ;
  822. VAR
  823. s: ARRAY [0..MaxErrorString] OF CHAR ;
  824. BEGIN
  825. StringFormat1 (s, a, w) ;
  826. FormatError (s)
  827. END FormatError1 ;
  828. (*
  829. FormatError2 - generic error procedure taking standard format string
  830. and two parameters.
  831. *)
  832. PROCEDURE FormatError2 (a: ARRAY OF CHAR;
  833. w1, w2: ARRAY OF BYTE) ;
  834. VAR
  835. s: ARRAY [0..MaxErrorString] OF CHAR ;
  836. BEGIN
  837. StringFormat1 (s, a, w1) ;
  838. FormatError1 (s, w2)
  839. END FormatError2 ;
  840. (*
  841. CheckAccess - checks to see whether a file f has been
  842. opened for read/write.
  843. *)
  844. PROCEDURE CheckAccess (f: File; use: FileUsage; towrite: BOOLEAN) ;
  845. VAR
  846. fd: FileDescriptor ;
  847. BEGIN
  848. IF f#Error
  849. THEN
  850. fd := GetIndice (FileInfo, f) ;
  851. IF fd=NIL
  852. THEN
  853. IF f#StdErr
  854. THEN
  855. FormatError ('this file has probably been closed and not reopened successfully or alternatively never opened\n')
  856. END ;
  857. HALT
  858. ELSE
  859. WITH fd^ DO
  860. IF (use=openedforwrite) AND (usage=openedforread)
  861. THEN
  862. FormatError1 ('this file (%s) has been opened for reading but is now being written\n',
  863. name.address) ;
  864. HALT
  865. ELSIF (use=openedforread) AND (usage=openedforwrite)
  866. THEN
  867. FormatError1('this file (%s) has been opened for writing but is now being read\n',
  868. name.address) ;
  869. HALT
  870. ELSIF state=connectionfailure
  871. THEN
  872. FormatError1('this file (%s) was not successfully opened\n',
  873. name.address) ;
  874. HALT
  875. ELSIF towrite#output
  876. THEN
  877. IF output
  878. THEN
  879. FormatError1('this file (%s) was opened for writing but is now being read\n',
  880. name.address) ;
  881. HALT
  882. ELSE
  883. FormatError1('this file (%s) was opened for reading but is now being written\n',
  884. name.address) ;
  885. HALT
  886. END
  887. END
  888. END
  889. END
  890. ELSE
  891. FormatError('this file has not been opened successfully\n') ;
  892. HALT
  893. END
  894. END CheckAccess ;
  895. (*
  896. ReadChar - returns a character read from file f.
  897. Sensible to check with IsNoError or EOF after calling
  898. this function.
  899. *)
  900. PROCEDURE ReadChar (f: File) : CHAR ;
  901. VAR
  902. ch: CHAR ;
  903. BEGIN
  904. CheckAccess (f, openedforread, FALSE) ;
  905. IF BufferedRead (f, SIZE (ch), ADR (ch)) = VAL (INTEGER, SIZE (ch))
  906. THEN
  907. SetEndOfLine (f, ch) ;
  908. RETURN ch
  909. ELSE
  910. RETURN nul
  911. END
  912. END ReadChar ;
  913. (*
  914. SetEndOfLine -
  915. *)
  916. PROCEDURE SetEndOfLine (f: File; ch: CHAR) ;
  917. VAR
  918. fd: FileDescriptor ;
  919. BEGIN
  920. CheckAccess(f, openedforread, FALSE) ;
  921. IF f#Error
  922. THEN
  923. fd := GetIndice(FileInfo, f) ;
  924. WITH fd^ DO
  925. IF ch=nl
  926. THEN
  927. state := endofline
  928. ELSE
  929. state := successful
  930. END
  931. END
  932. END
  933. END SetEndOfLine ;
  934. (*
  935. UnReadChar - replaces a character, ch, back into file f.
  936. This character must have been read by ReadChar
  937. and it does not allow successive calls. It may
  938. only be called if the previous read was successful
  939. or end of file was seen.
  940. If the state was previously endoffile then it
  941. is altered to successful.
  942. Otherwise it is left alone.
  943. *)
  944. PROCEDURE UnReadChar (f: File; ch: CHAR) ;
  945. VAR
  946. fd : FileDescriptor ;
  947. n : CARDINAL ;
  948. a, b: ADDRESS ;
  949. BEGIN
  950. CheckAccess(f, openedforread, FALSE) ;
  951. IF f#Error
  952. THEN
  953. fd := GetIndice(FileInfo, f) ;
  954. WITH fd^ DO
  955. IF (state=successful) OR (state=endoffile) OR (state=endofline)
  956. THEN
  957. IF (buffer#NIL) AND (buffer^.valid)
  958. THEN
  959. WITH buffer^ DO
  960. (* we assume that a ReadChar has occurred, we will check just in case. *)
  961. IF state=endoffile
  962. THEN
  963. position := MaxBufferLength ;
  964. left := 0 ;
  965. filled := 0 ;
  966. state := successful
  967. END ;
  968. IF position>0
  969. THEN
  970. DEC(position) ;
  971. INC(left) ;
  972. contents^[position] := ch ;
  973. ELSE
  974. (* position=0 *)
  975. (* if possible make room and store ch *)
  976. IF filled=size
  977. THEN
  978. FormatError1('performing too many UnReadChar calls on file (%d)\n', f)
  979. ELSE
  980. n := filled-position ;
  981. b := ADR(contents^[position]) ;
  982. a := ADR(contents^[position+1]) ;
  983. a := memcpy(a, b, n) ;
  984. INC(filled) ;
  985. contents^[position] := ch ;
  986. END
  987. END
  988. END
  989. END
  990. ELSE
  991. FormatError1('UnReadChar can only be called if the previous read was successful or end of file, error on file (%d)\n', f)
  992. END
  993. END
  994. END
  995. END UnReadChar ;
  996. (*
  997. ReadAny - reads HIGH (a) + 1 bytes into, a. All input
  998. is fully buffered, unlike ReadNBytes and thus is more
  999. suited to small reads.
  1000. *)
  1001. PROCEDURE ReadAny (f: File; VAR a: ARRAY OF BYTE) ;
  1002. BEGIN
  1003. CheckAccess(f, openedforread, FALSE) ;
  1004. IF BufferedRead (f, HIGH (a) + 1, ADR (a)) = VAL (INTEGER, HIGH (a) + 1)
  1005. THEN
  1006. SetEndOfLine (f, a[HIGH(a)])
  1007. END
  1008. END ReadAny ;
  1009. (*
  1010. EOF - tests to see whether a file, f, has reached end of file.
  1011. *)
  1012. PROCEDURE EOF (f: File) : BOOLEAN ;
  1013. VAR
  1014. fd: FileDescriptor ;
  1015. BEGIN
  1016. CheckAccess(f, openedforread, FALSE) ;
  1017. IF f#Error
  1018. THEN
  1019. fd := GetIndice(FileInfo, f) ;
  1020. IF fd#NIL
  1021. THEN
  1022. RETURN( fd^.state=endoffile )
  1023. END
  1024. END ;
  1025. RETURN( TRUE )
  1026. END EOF ;
  1027. (*
  1028. EOLN - tests to see whether a file, f, is upon a newline.
  1029. It does NOT consume the newline.
  1030. *)
  1031. PROCEDURE EOLN (f: File) : BOOLEAN ;
  1032. VAR
  1033. ch: CHAR ;
  1034. fd: FileDescriptor ;
  1035. BEGIN
  1036. CheckAccess(f, openedforread, FALSE) ;
  1037. (*
  1038. we will read a character and then push it back onto the input stream,
  1039. having noted the file status, we also reset the status.
  1040. *)
  1041. IF f#Error
  1042. THEN
  1043. fd := GetIndice(FileInfo, f) ;
  1044. IF fd#NIL
  1045. THEN
  1046. IF (fd^.state=successful) OR (fd^.state=endofline)
  1047. THEN
  1048. ch := ReadChar(f) ;
  1049. IF (fd^.state=successful) OR (fd^.state=endofline)
  1050. THEN
  1051. UnReadChar(f, ch)
  1052. END ;
  1053. RETURN( ch=nl )
  1054. END
  1055. END
  1056. END ;
  1057. RETURN( FALSE )
  1058. END EOLN ;
  1059. (*
  1060. WasEOLN - tests to see whether a file, f, has just seen a newline.
  1061. *)
  1062. PROCEDURE WasEOLN (f: File) : BOOLEAN ;
  1063. VAR
  1064. fd: FileDescriptor ;
  1065. BEGIN
  1066. CheckAccess(f, openedforread, FALSE) ;
  1067. IF f=Error
  1068. THEN
  1069. RETURN FALSE
  1070. ELSE
  1071. fd := GetIndice(FileInfo, f) ;
  1072. RETURN( (fd#NIL) AND (fd^.state=endofline) )
  1073. END
  1074. END WasEOLN ;
  1075. (*
  1076. WriteLine - writes out a linefeed to file, f.
  1077. *)
  1078. PROCEDURE WriteLine (f: File) ;
  1079. BEGIN
  1080. WriteChar(f, nl)
  1081. END WriteLine ;
  1082. (*
  1083. WriteNBytes - writes nBytes from memory area src to a file
  1084. returning the number of bytes actually written.
  1085. This function will flush the buffer and then
  1086. write the nBytes using a direct write from libc.
  1087. It is ideal for large writes.
  1088. *)
  1089. PROCEDURE WriteNBytes (f: File; nBytes: CARDINAL; src: ADDRESS) : CARDINAL ;
  1090. VAR
  1091. total: INTEGER ;
  1092. fd : FileDescriptor ;
  1093. BEGIN
  1094. CheckAccess(f, openedforwrite, TRUE) ;
  1095. FlushBuffer(f) ;
  1096. IF f#Error
  1097. THEN
  1098. fd := GetIndice(FileInfo, f) ;
  1099. IF fd#NIL
  1100. THEN
  1101. WITH fd^ DO
  1102. total := write(unixfd, src, INTEGER(nBytes)) ;
  1103. IF total<0
  1104. THEN
  1105. state := failed ;
  1106. RETURN( 0 )
  1107. ELSE
  1108. INC(abspos, CARDINAL(total)) ;
  1109. IF buffer#NIL
  1110. THEN
  1111. buffer^.bufstart := abspos
  1112. END ;
  1113. RETURN( CARDINAL(total) )
  1114. END
  1115. END
  1116. END
  1117. END ;
  1118. RETURN( 0 )
  1119. END WriteNBytes ;
  1120. (*
  1121. BufferedWrite - will write, nBytes, through the buffer.
  1122. Similar to WriteNBytes, but this function will always
  1123. write into the buffer before copying into memory.
  1124. Useful when performing small writes.
  1125. *)
  1126. PROCEDURE BufferedWrite (f: File; nBytes: CARDINAL; src: ADDRESS) : INTEGER ;
  1127. VAR
  1128. dest : ADDRESS ;
  1129. total,
  1130. n : INTEGER ;
  1131. p : POINTER TO BYTE ;
  1132. fd : FileDescriptor ;
  1133. BEGIN
  1134. IF f # Error
  1135. THEN
  1136. fd := GetIndice (FileInfo, f) ;
  1137. IF fd#NIL
  1138. THEN
  1139. total := 0 ; (* how many bytes have we read *)
  1140. WITH fd^ DO
  1141. IF buffer # NIL
  1142. THEN
  1143. WITH buffer^ DO
  1144. WHILE nBytes > 0 DO
  1145. (* place into the buffer first *)
  1146. IF left > 0
  1147. THEN
  1148. IF nBytes = 1
  1149. THEN
  1150. (* too expensive to call memcpy for 1 character *)
  1151. p := src ;
  1152. contents^[position] := p^ ;
  1153. DEC (left) ; (* reduce space *)
  1154. INC (position) ; (* move onwards n byte *)
  1155. INC (total) ;
  1156. RETURN( total )
  1157. ELSE
  1158. n := Min (left, nBytes) ;
  1159. dest := address ;
  1160. INC (dest, position) ;
  1161. p := memcpy (dest, src, CARDINAL (n)) ;
  1162. DEC (left, n) ; (* remove consumed bytes *)
  1163. INC (position, n) ; (* move onwards n bytes *)
  1164. (* move ready for further writes *)
  1165. INC (src, n) ;
  1166. DEC (nBytes, n) ; (* reduce the amount for future writes *)
  1167. INC (total, n)
  1168. END
  1169. ELSE
  1170. FlushBuffer (f) ;
  1171. IF (state#successful) AND (state#endofline)
  1172. THEN
  1173. nBytes := 0
  1174. END
  1175. END
  1176. END
  1177. END ;
  1178. RETURN( total )
  1179. END
  1180. END
  1181. END
  1182. END ;
  1183. RETURN( -1 )
  1184. END BufferedWrite ;
  1185. (*
  1186. FlushBuffer - flush contents of file, f.
  1187. *)
  1188. PROCEDURE FlushBuffer (f: File) ;
  1189. VAR
  1190. fd: FileDescriptor ;
  1191. BEGIN
  1192. IF f#Error
  1193. THEN
  1194. fd := GetIndice(FileInfo, f) ;
  1195. IF fd#NIL
  1196. THEN
  1197. WITH fd^ DO
  1198. IF output AND (buffer#NIL)
  1199. THEN
  1200. WITH buffer^ DO
  1201. IF (position=0) OR (write(unixfd, address, position)=VAL(INTEGER, position))
  1202. THEN
  1203. INC(abspos, position) ;
  1204. bufstart := abspos ;
  1205. position := 0 ;
  1206. filled := 0 ;
  1207. left := size
  1208. ELSE
  1209. state := failed
  1210. END
  1211. END
  1212. END
  1213. END
  1214. END
  1215. END
  1216. END FlushBuffer ;
  1217. (*
  1218. WriteAny - writes HIGH (a) + 1 bytes onto, file, f. All output
  1219. is fully buffered, unlike WriteNBytes and thus is more
  1220. suited to small writes.
  1221. *)
  1222. PROCEDURE WriteAny (f: File; VAR a: ARRAY OF BYTE) ;
  1223. BEGIN
  1224. CheckAccess (f, openedforwrite, TRUE) ;
  1225. IF BufferedWrite (f, HIGH (a) + 1, ADR (a)) = VAL (INTEGER, HIGH (a) + 1)
  1226. THEN
  1227. END
  1228. END WriteAny ;
  1229. (*
  1230. WriteChar - writes a single character to file, f.
  1231. *)
  1232. PROCEDURE WriteChar (f: File; ch: CHAR) ;
  1233. BEGIN
  1234. CheckAccess (f, openedforwrite, TRUE) ;
  1235. IF BufferedWrite (f, SIZE (ch), ADR (ch)) = VAL (INTEGER, SIZE (ch))
  1236. THEN
  1237. END
  1238. END WriteChar ;
  1239. (*
  1240. WriteCardinal - writes a CARDINAL to file, f.
  1241. It writes the binary image of the cardinal
  1242. to file, f.
  1243. *)
  1244. PROCEDURE WriteCardinal (f: File; c: CARDINAL) ;
  1245. BEGIN
  1246. WriteAny(f, c)
  1247. END WriteCardinal ;
  1248. (*
  1249. ReadCardinal - reads a CARDINAL from file, f.
  1250. It reads a binary image of a CARDINAL
  1251. from a file, f.
  1252. *)
  1253. PROCEDURE ReadCardinal (f: File) : CARDINAL ;
  1254. VAR
  1255. c: CARDINAL ;
  1256. BEGIN
  1257. ReadAny(f, c) ;
  1258. RETURN( c )
  1259. END ReadCardinal ;
  1260. (*
  1261. ReadString - reads a string from file, f, into string, a.
  1262. It terminates the string if HIGH is reached or
  1263. if a newline is seen or an error occurs.
  1264. *)
  1265. PROCEDURE ReadString (f: File; VAR a: ARRAY OF CHAR) ;
  1266. VAR
  1267. high,
  1268. i : CARDINAL ;
  1269. ch : CHAR ;
  1270. BEGIN
  1271. CheckAccess(f, openedforread, FALSE) ;
  1272. high := HIGH(a) ;
  1273. i := 0 ;
  1274. REPEAT
  1275. ch := ReadChar(f) ;
  1276. IF i<=high
  1277. THEN
  1278. IF (ch=nl) OR (NOT IsNoError(f)) OR EOF(f)
  1279. THEN
  1280. a[i] := nul ;
  1281. INC(i)
  1282. ELSE
  1283. a[i] := ch ;
  1284. INC(i)
  1285. END
  1286. END
  1287. UNTIL (ch=nl) OR (i>high) OR (NOT IsNoError(f)) OR EOF(f)
  1288. END ReadString ;
  1289. (*
  1290. SetPositionFromBeginning - sets the position from the beginning of the file.
  1291. *)
  1292. PROCEDURE SetPositionFromBeginning (f: File; pos: LONGINT) ;
  1293. VAR
  1294. offset: LONGINT ;
  1295. fd : FileDescriptor ;
  1296. BEGIN
  1297. IF f#Error
  1298. THEN
  1299. fd := GetIndice(FileInfo, f) ;
  1300. IF fd#NIL
  1301. THEN
  1302. WITH fd^ DO
  1303. (* always force the lseek, until we are confident that abspos is always correct,
  1304. basically it needs some hard testing before we should remove the OR TRUE. *)
  1305. IF (abspos#pos) OR TRUE
  1306. THEN
  1307. FlushBuffer(f) ;
  1308. IF buffer#NIL
  1309. THEN
  1310. WITH buffer^ DO
  1311. IF output
  1312. THEN
  1313. left := size
  1314. ELSE
  1315. left := 0
  1316. END ;
  1317. position := 0 ;
  1318. filled := 0
  1319. END
  1320. END ;
  1321. offset := lseek (unixfd, VAL (COFF_T, pos), SeekSet ()) ;
  1322. IF (offset>=0) AND (pos=offset)
  1323. THEN
  1324. abspos := pos
  1325. ELSE
  1326. state := failed ;
  1327. abspos := 0
  1328. END ;
  1329. IF buffer#NIL
  1330. THEN
  1331. buffer^.valid := FALSE ;
  1332. buffer^.bufstart := abspos
  1333. END
  1334. END
  1335. END
  1336. END
  1337. END
  1338. END SetPositionFromBeginning ;
  1339. (*
  1340. SetPositionFromEnd - sets the position from the end of the file.
  1341. *)
  1342. PROCEDURE SetPositionFromEnd (f: File; pos: LONGINT) ;
  1343. VAR
  1344. offset: LONGINT ;
  1345. fd : FileDescriptor ;
  1346. BEGIN
  1347. IF f#Error
  1348. THEN
  1349. fd := GetIndice(FileInfo, f) ;
  1350. IF fd#NIL
  1351. THEN
  1352. WITH fd^ DO
  1353. FlushBuffer(f) ;
  1354. IF buffer#NIL
  1355. THEN
  1356. WITH buffer^ DO
  1357. IF output
  1358. THEN
  1359. left := size
  1360. ELSE
  1361. left := 0
  1362. END ;
  1363. position := 0 ;
  1364. filled := 0
  1365. END
  1366. END ;
  1367. offset := lseek (unixfd, VAL (COFF_T, pos), SeekEnd ()) ;
  1368. IF offset>=0
  1369. THEN
  1370. abspos := offset ;
  1371. ELSE
  1372. state := failed ;
  1373. abspos := 0 ;
  1374. offset := 0
  1375. END ;
  1376. IF buffer#NIL
  1377. THEN
  1378. buffer^.valid := FALSE ;
  1379. buffer^.bufstart := offset
  1380. END
  1381. END
  1382. END
  1383. END
  1384. END SetPositionFromEnd ;
  1385. (*
  1386. FindPosition - returns the current absolute position in file, f.
  1387. *)
  1388. PROCEDURE FindPosition (f: File) : LONGINT ;
  1389. VAR
  1390. fd: FileDescriptor ;
  1391. BEGIN
  1392. IF f#Error
  1393. THEN
  1394. fd := GetIndice(FileInfo, f) ;
  1395. IF fd#NIL
  1396. THEN
  1397. WITH fd^ DO
  1398. IF (buffer=NIL) OR (NOT buffer^.valid)
  1399. THEN
  1400. RETURN( abspos )
  1401. ELSE
  1402. WITH buffer^ DO
  1403. RETURN( bufstart+VAL(LONGINT, position) )
  1404. END
  1405. END
  1406. END
  1407. END
  1408. END ;
  1409. RETURN( 0 )
  1410. END FindPosition ;
  1411. (*
  1412. GetFDesc - return the file descriptor associated with File name, fname
  1413. *)
  1414. PROCEDURE GetFDesc (fname : ARRAY OF CHAR ) : File;
  1415. VAR
  1416. i : CARDINAL;
  1417. fd : File;
  1418. name : ARRAY[0..256] OF CHAR;
  1419. BEGIN
  1420. FOR i := LowIndice(FileInfo) TO HighIndice(FileInfo) DO
  1421. fd := GetIndice (FileInfo, i);
  1422. GetFileName(fd, name);
  1423. IF Strings.Compare (fname,name) = equal THEN
  1424. RETURN fd
  1425. END;
  1426. END;
  1427. RETURN NIL
  1428. END GetFDesc;
  1429. (*
  1430. GetFileName - assigns, a, with the filename associated with, f.
  1431. *)
  1432. PROCEDURE GetFileName (f: File; VAR a: ARRAY OF CHAR) ;
  1433. VAR
  1434. i : CARDINAL ;
  1435. p : POINTER TO CHAR ;
  1436. fd: FileDescriptor ;
  1437. BEGIN
  1438. IF f#Error
  1439. THEN
  1440. fd := GetIndice(FileInfo, f) ;
  1441. IF fd=NIL
  1442. THEN
  1443. FormatError('this file has probably been closed and not reopened successfully or alternatively never opened\n') ;
  1444. HALT
  1445. ELSE
  1446. WITH fd^.name DO
  1447. IF address=NIL
  1448. THEN
  1449. StrCopy('', a)
  1450. ELSE
  1451. p := address ;
  1452. i := 0 ;
  1453. WHILE (p^#nul) AND (i<=HIGH(a)) DO
  1454. a[i] := p^ ;
  1455. INC(p) ;
  1456. INC(i)
  1457. END
  1458. END
  1459. END
  1460. END
  1461. END
  1462. END GetFileName ;
  1463. (*
  1464. getFileName - returns the address of the filename associated with, f.
  1465. *)
  1466. PROCEDURE getFileName (f: File) : ADDRESS ;
  1467. VAR
  1468. fd: FileDescriptor ;
  1469. BEGIN
  1470. IF f#Error
  1471. THEN
  1472. fd := GetIndice(FileInfo, f) ;
  1473. IF fd=NIL
  1474. THEN
  1475. FormatError('this file has probably been closed and not reopened successfully or alternatively never opened\n') ;
  1476. HALT
  1477. ELSE
  1478. RETURN fd^.name.address
  1479. END
  1480. END ;
  1481. RETURN NIL
  1482. END getFileName ;
  1483. (*
  1484. getFileNameLength - returns the number of characters associated with filename, f.
  1485. *)
  1486. PROCEDURE getFileNameLength (f: File) : CARDINAL ;
  1487. VAR
  1488. fd: FileDescriptor ;
  1489. BEGIN
  1490. IF f#Error
  1491. THEN
  1492. fd := GetIndice(FileInfo, f) ;
  1493. IF fd=NIL
  1494. THEN
  1495. FormatError('this file has probably been closed and not reopened successfully or alternatively never opened\n') ;
  1496. HALT
  1497. ELSE
  1498. RETURN fd^.name.size
  1499. END
  1500. END ;
  1501. RETURN 0
  1502. END getFileNameLength ;
  1503. (*
  1504. PreInitialize - preinitialize the file descriptor.
  1505. *)
  1506. PROCEDURE PreInitialize (f: File; fname: ARRAY OF CHAR;
  1507. state: FileStatus; use: FileUsage;
  1508. towrite: BOOLEAN; osfd: INTEGER; bufsize: CARDINAL) ;
  1509. VAR
  1510. fd, fe: FileDescriptor ;
  1511. BEGIN
  1512. IF InitializeFile(f, ADR(fname), StrLen(fname), state, use, towrite, bufsize)=f
  1513. THEN
  1514. fd := GetIndice(FileInfo, f) ;
  1515. IF f=Error
  1516. THEN
  1517. fe := GetIndice(FileInfo, StdErr) ;
  1518. IF fe=NIL
  1519. THEN
  1520. HALT
  1521. ELSE
  1522. fd^.unixfd := fe^.unixfd (* the error channel *)
  1523. END
  1524. ELSE
  1525. fd^.unixfd := osfd
  1526. END
  1527. ELSE
  1528. HALT
  1529. END
  1530. END PreInitialize ;
  1531. (*
  1532. FlushOutErr - flushes, StdOut, and, StdErr.
  1533. It is also called when the application calls M2RTS.Terminate.
  1534. (which is automatically placed in program modules by the GM2
  1535. scaffold).
  1536. *)
  1537. PROCEDURE FlushOutErr ;
  1538. BEGIN
  1539. IF IsNoError(StdOut)
  1540. THEN
  1541. FlushBuffer(StdOut)
  1542. END ;
  1543. IF IsNoError(StdErr)
  1544. THEN
  1545. FlushBuffer(StdErr)
  1546. END
  1547. END FlushOutErr ;
  1548. (*
  1549. Init - initialize the modules, global variables.
  1550. *)
  1551. PROCEDURE Init ;
  1552. BEGIN
  1553. FileInfo := InitIndex(0) ;
  1554. Error := 0 ;
  1555. PreInitialize(Error , 'error' , toomanyfilesopen, unused , FALSE, -1, 0) ;
  1556. StdIn := 1 ;
  1557. PreInitialize(StdIn , '<stdin>' , successful , openedforread , FALSE, 0, MaxBufferLength) ;
  1558. StdOut := 2 ;
  1559. PreInitialize(StdOut , '<stdout>', successful , openedforwrite, TRUE, 1, MaxBufferLength) ;
  1560. StdErr := 3 ;
  1561. PreInitialize(StdErr , '<stderr>', successful , openedforwrite, TRUE, 2, MaxBufferLength) ;
  1562. IF NOT InstallTerminationProcedure(FlushOutErr)
  1563. THEN
  1564. HALT
  1565. END
  1566. END Init ;
  1567. BEGIN
  1568. Init
  1569. FINALLY
  1570. FlushOutErr
  1571. END FIO.