fileio.ck 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  1. /*++
  2. Copyright (c) 2016 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. fileio.ck
  9. Abstract:
  10. This module implements support for performing file I/O. It requires the os
  11. module. This module is inspired by Python's FileIO module.
  12. Author:
  13. Evan Green 10-Jan-2016
  14. Environment:
  15. Chalk
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. from iobase import RawIoBase, IoError, IO_SEEK_SET, IO_SEEK_CUR, IO_SEEK_END,
  21. DEFAULT_BUFFER_SIZE;
  22. from os import O_RDONLY, O_WRONLY, O_APPEND, O_RDWR, O_TRUNC, O_CREAT, O_TEXT,
  23. O_BINARY, OS_SEEK_SET, OS_SEEK_CUR, OS_SEEK_END;
  24. from os import EINTR, EAGAIN;
  25. from os import OsError, open, close, read, write, lseek, isatty, ftruncate,
  26. fstat;
  27. //
  28. // ---------------------------------------------------------------- Definitions
  29. //
  30. //
  31. // ----------------------------------------------- Internal Function Prototypes
  32. //
  33. //
  34. // -------------------------------------------------------------------- Globals
  35. //
  36. //
  37. // ------------------------------------------------------------------ Functions
  38. //
  39. //
  40. // FileIo is the RawIoBase implementation for the os layer.
  41. //
  42. class FileIo is RawIoBase {
  43. var _fd;
  44. var _readable;
  45. var _writable;
  46. var _appending;
  47. var _seekable;
  48. var _closefd;
  49. function
  50. __init (
  51. name,
  52. mode,
  53. permissions,
  54. closefd
  55. )
  56. /*++
  57. Routine Description:
  58. This routine instantiates a new FileIo object.
  59. Arguments:
  60. name - Supplies the thing to open. If this is a string, this represents
  61. the path to open. If this is an integer, it represents the os file
  62. descriptor to open.
  63. mode - Supplies the access mode. Valid values are 'r' for read, 'w' for
  64. write, or 'a' for append. Add a plus to get simultaneous reading
  65. and writing.
  66. permissions - Supplies the permissions to create the file with for
  67. creations. Ignored if the file is not created.
  68. closefd - Supplies a boolean indicating whether the file descriptor (if
  69. passed in via the name parameter) should be closed when the stream
  70. is closed.
  71. Return Value:
  72. Returns the initialized object.
  73. --*/
  74. {
  75. var access = -1;
  76. var accessCount = 0;
  77. var character;
  78. var flags = 0;
  79. var index = 0;
  80. var modeLength = mode.length();
  81. var plus = 0;
  82. if (_closefd) {
  83. this._close();
  84. }
  85. _fd = -1;
  86. _readable = false;
  87. _writable = false;
  88. _appending = false;
  89. _seekable = -1;
  90. _closefd = true;
  91. this.mode = mode;
  92. this.name = name;
  93. this.closed = false;
  94. //
  95. // Figure out the mode from the string.
  96. //
  97. for (index = 0; index < modeLength; index += 1) {
  98. character = mode[index];
  99. if (character == "r") {
  100. accessCount += 1;
  101. _readable = true;
  102. access = O_RDONLY;
  103. } else if (character == "w") {
  104. accessCount += 1;
  105. _writable = true;
  106. access = O_WRONLY;
  107. flags |= O_CREAT | O_TRUNC;
  108. } else if (character == "a") {
  109. accessCount += 1;
  110. _writable = true;
  111. _appending = true;
  112. access = O_WRONLY;
  113. flags |= O_CREAT | O_APPEND;
  114. } else if (character == "+") {
  115. _readable = true;
  116. _writable = true;
  117. plus += 1;
  118. access = O_RDWR;
  119. } else if (character == "b") {
  120. flags &= ~O_TEXT;
  121. flags |= O_BINARY;
  122. } else if (character == "t") {
  123. flags |= O_TEXT;
  124. flags &= ~O_BINARY;
  125. } else {
  126. Core.raise(ValueError("Invalid mode: %s" % mode));
  127. }
  128. }
  129. if ((accessCount != 1) || (plus > 1)) {
  130. Core.raise(ValueError("Mode must have exactly one r/w/a and "
  131. "at most one plus"));
  132. }
  133. //
  134. // If the name is an integer, use it as the file descriptor.
  135. //
  136. if (name is Int) {
  137. if (name < 0) {
  138. Core.raise(ValueError("Negative file descriptor passed"));
  139. }
  140. _fd = name;
  141. _closefd = closefd;
  142. } else if (name is String) {
  143. if (!closefd) {
  144. Core.raise(ValueError("Specify true for closefd when opening a "
  145. "file path"));
  146. }
  147. try {
  148. _fd = open(name, access | flags, permissions);
  149. } except OsError as e {
  150. this._raiseIoError(e);
  151. }
  152. } else {
  153. Core.raise(TypeError("Expected an integer or a string"));
  154. }
  155. if (_appending) {
  156. try {
  157. lseek(_fd, 0, OS_SEEK_END);
  158. } except OsError {}
  159. }
  160. return this;
  161. }
  162. //
  163. // TODO: Add __del method support to Chalk.
  164. //
  165. function
  166. __del (
  167. )
  168. /*++
  169. Routine Description:
  170. This routine is called when the object is going to be deleted.
  171. Arguments:
  172. None.
  173. Return Value:
  174. None.
  175. --*/
  176. {
  177. this._close();
  178. return;
  179. }
  180. function
  181. __str (
  182. )
  183. /*++
  184. Routine Description:
  185. This routine converts the file IO object into a string.
  186. Arguments:
  187. None.
  188. Return Value:
  189. None.
  190. --*/
  191. {
  192. return "<FileIo name=%s fd=%d mode=%s>" % [this.name, _fd, this.mode];
  193. }
  194. //
  195. // RawIoBase functions.
  196. //
  197. function
  198. read (
  199. size
  200. )
  201. /*++
  202. Routine Description:
  203. This routine performs a single read.
  204. Arguments:
  205. size - Supplies the amount to read. If -1 is supplied, then readall is
  206. called.
  207. Return Value:
  208. Returns the bytes read. If the empty string is returned and more than
  209. zero bytes were requested, then this is end-of-file.
  210. --*/
  211. {
  212. var data = "";
  213. if (_fd < 0) {
  214. Core.raise(ValueError("I/O operation on closed file"));
  215. }
  216. if (!_readable) {
  217. this._raiseModeError("reading");
  218. }
  219. if (size < 0) {
  220. return this.readall();
  221. }
  222. while (true) {
  223. try {
  224. data = read(_fd, size);
  225. } except OsError as e {
  226. if (e.errno == EINTR) {
  227. continue;
  228. } else if (e.errno == EAGAIN) {
  229. break;
  230. }
  231. this._raiseIoError(e);
  232. }
  233. break;
  234. }
  235. return data;
  236. }
  237. function
  238. readall (
  239. )
  240. /*++
  241. Routine Description:
  242. This routine reads the entire file.
  243. Arguments:
  244. None.
  245. Return Value:
  246. Returns the contents of the entire file.
  247. --*/
  248. {
  249. var bufferSize;
  250. var data;
  251. var result = [];
  252. var total = 0;
  253. if (_fd < 0) {
  254. Core.raise(ValueError("I/O operation on closed file"));
  255. }
  256. while (true) {
  257. bufferSize = this._estimateBufferSize(total);
  258. try {
  259. data = read(_fd, bufferSize);
  260. if (data.length() == 0) {
  261. break;
  262. }
  263. result.append(data);
  264. total += data.length();
  265. } except OsError as e {
  266. if (e.errno == EINTR) {
  267. continue;
  268. } else if (e.errno == EAGAIN) {
  269. if (total) {
  270. break;
  271. }
  272. }
  273. this._raiseIoError(e);
  274. }
  275. }
  276. return "".join(result);
  277. }
  278. function
  279. write (
  280. data
  281. )
  282. /*++
  283. Routine Description:
  284. This routine performs a single write.
  285. Arguments:
  286. size - Supplies the string of data to write.
  287. Return Value:
  288. Returns the number of bytes written, which may be less than the size of
  289. the data.
  290. --*/
  291. {
  292. var size = 0;
  293. if (_fd < 0) {
  294. Core.raise(ValueError("I/O operation on closed file"));
  295. }
  296. if (!_writable) {
  297. this._raiseModeError("writing");
  298. }
  299. while (true) {
  300. try {
  301. size = write(_fd, data);
  302. } except OsError as e {
  303. if (e.errno == EINTR) {
  304. continue;
  305. } else if (e.errno == EAGAIN) {
  306. break;
  307. }
  308. this._raiseIoError(e);
  309. }
  310. break;
  311. }
  312. return size;
  313. }
  314. //
  315. // IoBase functions.
  316. //
  317. function
  318. close (
  319. )
  320. /*++
  321. Routine Description:
  322. This routine closes and flushes a stream. If the stream is already
  323. closed, this does nothing.
  324. Arguments:
  325. None.
  326. Return Value:
  327. None.
  328. --*/
  329. {
  330. if (!this.closed) {
  331. this.flush();
  332. this._close();
  333. }
  334. return 0;
  335. }
  336. function
  337. fileno (
  338. )
  339. /*++
  340. Routine Description:
  341. This routine returns the OS file descriptor number associated with the
  342. stream.
  343. Arguments:
  344. None.
  345. Return Value:
  346. Returns the file descriptor number associated with the stream.
  347. -1 if no file descriptor is associated with the stream or the file has
  348. been closed.
  349. --*/
  350. {
  351. if (_fd < 0) {
  352. Core.raise(ValueError("I/O operation on closed file"));
  353. }
  354. return _fd;
  355. }
  356. function
  357. isatty (
  358. )
  359. /*++
  360. Routine Description:
  361. This routine determines if the file descriptor backing this stream is
  362. a terminal device.
  363. Arguments:
  364. None.
  365. Return Value:
  366. true if the file descriptor is a terminal device.
  367. false if the file descriptor is not a terminal.
  368. --*/
  369. {
  370. var result = false;
  371. if (_fd < 0) {
  372. Core.raise(ValueError("I/O operation on closed file"));
  373. }
  374. try {
  375. if (isatty(_fd)) {
  376. result = true;
  377. }
  378. } except OsError {}
  379. return result;
  380. }
  381. function
  382. isReadable (
  383. )
  384. /*++
  385. Routine Description:
  386. This routine determines if the given stream can be read from.
  387. Arguments:
  388. None.
  389. Return Value:
  390. true if the stream was opened with read permissions.
  391. false if the stream cannot be read from.
  392. --*/
  393. {
  394. if (_fd < 0) {
  395. Core.raise(ValueError("I/O operation on closed file"));
  396. }
  397. return _readable;
  398. }
  399. function
  400. seek (
  401. offset,
  402. whence
  403. )
  404. /*++
  405. Routine Description:
  406. This routine seeks into the file.
  407. Arguments:
  408. offset - Supplies the desired offset to seek from or to.
  409. whence - Supplies the anchor point for the seek. Supply IO_SEEK_SET to
  410. seek from the beginning of the file, IO_SEEK_CUR to seek from the
  411. current position, or IO_SEEK_END to seek from the end of the file.
  412. Return Value:
  413. Returns the new absolute position within the file.
  414. --*/
  415. {
  416. var osWhence;
  417. if (_fd < 0) {
  418. Core.raise(ValueError("I/O operation on closed file"));
  419. }
  420. if (whence == IO_SEEK_SET) {
  421. osWhence = OS_SEEK_SET;
  422. } else if (whence == IO_SEEK_CUR) {
  423. osWhence = OS_SEEK_CUR;
  424. } else if (whence == IO_SEEK_END) {
  425. osWhence = OS_SEEK_END;
  426. }
  427. return lseek(_fd, offset, osWhence);
  428. }
  429. function
  430. isSeekable (
  431. )
  432. /*++
  433. Routine Description:
  434. This routine determines if the file backing the stream can be seeked on.
  435. Arguments:
  436. None.
  437. Return Value:
  438. true if the underlying file is seekable.
  439. false if the underlying file is not seekable.
  440. --*/
  441. {
  442. if (_fd < 0) {
  443. Core.raise(ValueError("I/O operation on closed file"));
  444. }
  445. if (_seekable < 0) {
  446. try {
  447. lseek(_fd, 0, OS_SEEK_CUR);
  448. _seekable = true;
  449. } except OsError {
  450. _seekable = false;
  451. }
  452. }
  453. return _seekable;
  454. }
  455. function
  456. tell (
  457. )
  458. /*++
  459. Routine Description:
  460. This routine returns the current file position.
  461. Arguments:
  462. None.
  463. Return Value:
  464. Returns the current file position.
  465. --*/
  466. {
  467. if (_fd < 0) {
  468. Core.raise(ValueError("I/O operation on closed file"));
  469. }
  470. return this.seek(0, IO_SEEK_CUR);
  471. }
  472. function
  473. truncate (
  474. size
  475. )
  476. /*++
  477. Routine Description:
  478. This routine truncates the file to the given size. The stream position
  479. is not changed.
  480. Arguments:
  481. size - Supplies the new size of the file.
  482. Return Value:
  483. Returns the new file size.
  484. --*/
  485. {
  486. if (_fd < 0) {
  487. Core.raise(ValueError("I/O operation on closed file"));
  488. }
  489. if (!_writable) {
  490. this._raiseModeError("writing");
  491. }
  492. try {
  493. ftruncate(_fd, size);
  494. } except OsError as e {
  495. this._raiseIoError(e);
  496. }
  497. return size;
  498. }
  499. function
  500. isWritable (
  501. )
  502. /*++
  503. Routine Description:
  504. This routine determines if the given file can be written to.
  505. Arguments:
  506. None.
  507. Return Value:
  508. true if the file can be written to.
  509. false if the file cannot be written to.
  510. --*/
  511. {
  512. if (_fd < 0) {
  513. Core.raise(ValueError("I/O operation on closed file"));
  514. }
  515. return _writable;
  516. }
  517. //
  518. // Internal functions
  519. //
  520. function
  521. _close (
  522. )
  523. /*++
  524. Routine Description:
  525. This routine closes and flushes a stream.
  526. Arguments:
  527. None.
  528. Return Value:
  529. None.
  530. --*/
  531. {
  532. if (_fd >= 0) {
  533. try {
  534. close(_fd);
  535. _fd = -1;
  536. } except OsError as e {
  537. this._raiseIoError(e);
  538. }
  539. }
  540. this.closed = true;
  541. return 0;
  542. }
  543. function
  544. _estimateBufferSize (
  545. currentSize
  546. )
  547. /*++
  548. Routine Description:
  549. This routine returns a buffer to hold the file.
  550. Arguments:
  551. currentSize - Supplies the current size of the buffer.
  552. Return Value:
  553. None.
  554. --*/
  555. {
  556. var end = 0;
  557. var fileInfo;
  558. var position;
  559. try {
  560. fileInfo = fstat(_fd);
  561. end = fileInfo.st_size;
  562. position = lseek(_fd, 0, OS_SEEK_CUR);
  563. if ((end > DEFAULT_BUFFER_SIZE) && (end >= position) &&
  564. (position >= 0)) {
  565. return currentSize + (end - position) + 1;
  566. }
  567. } except OsError {}
  568. if (currentSize <= 0) {
  569. return DEFAULT_BUFFER_SIZE;
  570. }
  571. return currentSize + (currentSize >> 2) + 32;
  572. }
  573. function
  574. _raiseModeError (
  575. mode
  576. )
  577. /*++
  578. Routine Description:
  579. This routine raises an error for an operation the file is not open for.
  580. Arguments:
  581. mode - Supplies the mode string to print in the error.
  582. Return Value:
  583. None.
  584. --*/
  585. {
  586. Core.raise(ValueError("File not open for " + mode));
  587. }
  588. function
  589. _raiseIoError (
  590. oserror
  591. )
  592. /*++
  593. Routine Description:
  594. This routine raises an I/O error from an OS error.
  595. Arguments:
  596. oserror - Supplies the OS error.
  597. Return Value:
  598. None.
  599. --*/
  600. {
  601. var ioerror = IoError(oserror.args);
  602. ioerror.errno = oserror.errno;
  603. Core.raise(ioerror);
  604. return;
  605. }
  606. }
  607. //
  608. // --------------------------------------------------------- Internal Functions
  609. //