123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928 |
- /*++
- Copyright (c) 2016 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- fileio.ck
- Abstract:
- This module implements support for performing file I/O. It requires the os
- module. This module is inspired by Python's FileIO module.
- Author:
- Evan Green 10-Jan-2016
- Environment:
- Chalk
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- from iobase import RawIoBase, IoError, IO_SEEK_SET, IO_SEEK_CUR, IO_SEEK_END,
- DEFAULT_BUFFER_SIZE;
- from os import O_RDONLY, O_WRONLY, O_APPEND, O_RDWR, O_TRUNC, O_CREAT, O_TEXT,
- O_BINARY, OS_SEEK_SET, OS_SEEK_CUR, OS_SEEK_END;
- from os import EINTR, EAGAIN;
- from os import OsError, open, close, read, write, lseek, isatty, ftruncate,
- fstat;
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- //
- // FileIo is the RawIoBase implementation for the os layer.
- //
- class FileIo is RawIoBase {
- var _fd;
- var _readable;
- var _writable;
- var _appending;
- var _seekable;
- var _closefd;
- function
- __init (
- name,
- mode,
- permissions,
- closefd
- )
- /*++
- Routine Description:
- This routine instantiates a new FileIo object.
- Arguments:
- name - Supplies the thing to open. If this is a string, this represents
- the path to open. If this is an integer, it represents the os file
- descriptor to open.
- mode - Supplies the access mode. Valid values are 'r' for read, 'w' for
- write, or 'a' for append. Add a plus to get simultaneous reading
- and writing.
- permissions - Supplies the permissions to create the file with for
- creations. Ignored if the file is not created.
- closefd - Supplies a boolean indicating whether the file descriptor (if
- passed in via the name parameter) should be closed when the stream
- is closed.
- Return Value:
- Returns the initialized object.
- --*/
- {
- var access = -1;
- var accessCount = 0;
- var character;
- var flags = 0;
- var index = 0;
- var modeLength = mode.length();
- var plus = 0;
- if (_closefd) {
- this._close();
- }
- _fd = -1;
- _readable = false;
- _writable = false;
- _appending = false;
- _seekable = -1;
- _closefd = true;
- this.mode = mode;
- this.name = name;
- this.closed = false;
- //
- // Figure out the mode from the string.
- //
- for (index = 0; index < modeLength; index += 1) {
- character = mode[index];
- if (character == "r") {
- accessCount += 1;
- _readable = true;
- access = O_RDONLY;
- } else if (character == "w") {
- accessCount += 1;
- _writable = true;
- access = O_WRONLY;
- flags |= O_CREAT | O_TRUNC;
- } else if (character == "a") {
- accessCount += 1;
- _writable = true;
- _appending = true;
- access = O_WRONLY;
- flags |= O_CREAT | O_APPEND;
- } else if (character == "+") {
- _readable = true;
- _writable = true;
- plus += 1;
- access = O_RDWR;
- } else if (character == "b") {
- flags &= ~O_TEXT;
- flags |= O_BINARY;
- } else if (character == "t") {
- flags |= O_TEXT;
- flags &= ~O_BINARY;
- } else {
- Core.raise(ValueError("Invalid mode: %s" % mode));
- }
- }
- if ((accessCount != 1) || (plus > 1)) {
- Core.raise(ValueError("Mode must have exactly one r/w/a and "
- "at most one plus"));
- }
- //
- // If the name is an integer, use it as the file descriptor.
- //
- if (name is Int) {
- if (name < 0) {
- Core.raise(ValueError("Negative file descriptor passed"));
- }
- _fd = name;
- _closefd = closefd;
- } else if (name is String) {
- if (!closefd) {
- Core.raise(ValueError("Specify true for closefd when opening a "
- "file path"));
- }
- try {
- _fd = open(name, access | flags, permissions);
- } except OsError as e {
- this._raiseIoError(e);
- }
- } else {
- Core.raise(TypeError("Expected an integer or a string"));
- }
- if (_appending) {
- try {
- lseek(_fd, 0, OS_SEEK_END);
- } except OsError {}
- }
- return this;
- }
- //
- // TODO: Add __del method support to Chalk.
- //
- function
- __del (
- )
- /*++
- Routine Description:
- This routine is called when the object is going to be deleted.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- this._close();
- return;
- }
- function
- __str (
- )
- /*++
- Routine Description:
- This routine converts the file IO object into a string.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- return "<FileIo name=%s fd=%d mode=%s>" % [this.name, _fd, this.mode];
- }
- //
- // RawIoBase functions.
- //
- function
- read (
- size
- )
- /*++
- Routine Description:
- This routine performs a single read.
- Arguments:
- size - Supplies the amount to read. If -1 is supplied, then readall is
- called.
- Return Value:
- Returns the bytes read. If the empty string is returned and more than
- zero bytes were requested, then this is end-of-file.
- --*/
- {
- var data = "";
- if (_fd < 0) {
- Core.raise(ValueError("I/O operation on closed file"));
- }
- if (!_readable) {
- this._raiseModeError("reading");
- }
- if (size < 0) {
- return this.readall();
- }
- while (true) {
- try {
- data = read(_fd, size);
- } except OsError as e {
- if (e.errno == EINTR) {
- continue;
- } else if (e.errno == EAGAIN) {
- break;
- }
- this._raiseIoError(e);
- }
- break;
- }
- return data;
- }
- function
- readall (
- )
- /*++
- Routine Description:
- This routine reads the entire file.
- Arguments:
- None.
- Return Value:
- Returns the contents of the entire file.
- --*/
- {
- var bufferSize;
- var data;
- var result = [];
- var total = 0;
- if (_fd < 0) {
- Core.raise(ValueError("I/O operation on closed file"));
- }
- while (true) {
- bufferSize = this._estimateBufferSize(total);
- try {
- data = read(_fd, bufferSize);
- if (data.length() == 0) {
- break;
- }
- result.append(data);
- total += data.length();
- } except OsError as e {
- if (e.errno == EINTR) {
- continue;
- } else if (e.errno == EAGAIN) {
- if (total) {
- break;
- }
- }
- this._raiseIoError(e);
- }
- }
- return "".join(result);
- }
- function
- write (
- data
- )
- /*++
- Routine Description:
- This routine performs a single write.
- Arguments:
- size - Supplies the string of data to write.
- Return Value:
- Returns the number of bytes written, which may be less than the size of
- the data.
- --*/
- {
- var size = 0;
- if (_fd < 0) {
- Core.raise(ValueError("I/O operation on closed file"));
- }
- if (!_writable) {
- this._raiseModeError("writing");
- }
- while (true) {
- try {
- size = write(_fd, data);
- } except OsError as e {
- if (e.errno == EINTR) {
- continue;
- } else if (e.errno == EAGAIN) {
- break;
- }
- this._raiseIoError(e);
- }
- break;
- }
- return size;
- }
- //
- // IoBase functions.
- //
- function
- close (
- )
- /*++
- Routine Description:
- This routine closes and flushes a stream. If the stream is already
- closed, this does nothing.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- if (!this.closed) {
- this.flush();
- this._close();
- }
- return 0;
- }
- function
- fileno (
- )
- /*++
- Routine Description:
- This routine returns the OS file descriptor number associated with the
- stream.
- Arguments:
- None.
- Return Value:
- Returns the file descriptor number associated with the stream.
- -1 if no file descriptor is associated with the stream or the file has
- been closed.
- --*/
- {
- if (_fd < 0) {
- Core.raise(ValueError("I/O operation on closed file"));
- }
- return _fd;
- }
- function
- isatty (
- )
- /*++
- Routine Description:
- This routine determines if the file descriptor backing this stream is
- a terminal device.
- Arguments:
- None.
- Return Value:
- true if the file descriptor is a terminal device.
- false if the file descriptor is not a terminal.
- --*/
- {
- var result = false;
- if (_fd < 0) {
- Core.raise(ValueError("I/O operation on closed file"));
- }
- try {
- if (isatty(_fd)) {
- result = true;
- }
- } except OsError {}
- return result;
- }
- function
- isReadable (
- )
- /*++
- Routine Description:
- This routine determines if the given stream can be read from.
- Arguments:
- None.
- Return Value:
- true if the stream was opened with read permissions.
- false if the stream cannot be read from.
- --*/
- {
- if (_fd < 0) {
- Core.raise(ValueError("I/O operation on closed file"));
- }
- return _readable;
- }
- function
- seek (
- offset,
- whence
- )
- /*++
- Routine Description:
- This routine seeks into the file.
- Arguments:
- offset - Supplies the desired offset to seek from or to.
- whence - Supplies the anchor point for the seek. Supply IO_SEEK_SET to
- seek from the beginning of the file, IO_SEEK_CUR to seek from the
- current position, or IO_SEEK_END to seek from the end of the file.
- Return Value:
- Returns the new absolute position within the file.
- --*/
- {
- var osWhence;
- if (_fd < 0) {
- Core.raise(ValueError("I/O operation on closed file"));
- }
- if (whence == IO_SEEK_SET) {
- osWhence = OS_SEEK_SET;
- } else if (whence == IO_SEEK_CUR) {
- osWhence = OS_SEEK_CUR;
- } else if (whence == IO_SEEK_END) {
- osWhence = OS_SEEK_END;
- }
- return lseek(_fd, offset, osWhence);
- }
- function
- isSeekable (
- )
- /*++
- Routine Description:
- This routine determines if the file backing the stream can be seeked on.
- Arguments:
- None.
- Return Value:
- true if the underlying file is seekable.
- false if the underlying file is not seekable.
- --*/
- {
- if (_fd < 0) {
- Core.raise(ValueError("I/O operation on closed file"));
- }
- if (_seekable < 0) {
- try {
- lseek(_fd, 0, OS_SEEK_CUR);
- _seekable = true;
- } except OsError {
- _seekable = false;
- }
- }
- return _seekable;
- }
- function
- tell (
- )
- /*++
- Routine Description:
- This routine returns the current file position.
- Arguments:
- None.
- Return Value:
- Returns the current file position.
- --*/
- {
- if (_fd < 0) {
- Core.raise(ValueError("I/O operation on closed file"));
- }
- return this.seek(0, IO_SEEK_CUR);
- }
- function
- truncate (
- size
- )
- /*++
- Routine Description:
- This routine truncates the file to the given size. The stream position
- is not changed.
- Arguments:
- size - Supplies the new size of the file.
- Return Value:
- Returns the new file size.
- --*/
- {
- if (_fd < 0) {
- Core.raise(ValueError("I/O operation on closed file"));
- }
- if (!_writable) {
- this._raiseModeError("writing");
- }
- try {
- ftruncate(_fd, size);
- } except OsError as e {
- this._raiseIoError(e);
- }
- return size;
- }
- function
- isWritable (
- )
- /*++
- Routine Description:
- This routine determines if the given file can be written to.
- Arguments:
- None.
- Return Value:
- true if the file can be written to.
- false if the file cannot be written to.
- --*/
- {
- if (_fd < 0) {
- Core.raise(ValueError("I/O operation on closed file"));
- }
- return _writable;
- }
- //
- // Internal functions
- //
- function
- _close (
- )
- /*++
- Routine Description:
- This routine closes and flushes a stream.
- Arguments:
- None.
- Return Value:
- None.
- --*/
- {
- if (_fd >= 0) {
- try {
- close(_fd);
- _fd = -1;
- } except OsError as e {
- this._raiseIoError(e);
- }
- }
- this.closed = true;
- return 0;
- }
- function
- _estimateBufferSize (
- currentSize
- )
- /*++
- Routine Description:
- This routine returns a buffer to hold the file.
- Arguments:
- currentSize - Supplies the current size of the buffer.
- Return Value:
- None.
- --*/
- {
- var end = 0;
- var fileInfo;
- var position;
- try {
- fileInfo = fstat(_fd);
- end = fileInfo.st_size;
- position = lseek(_fd, 0, OS_SEEK_CUR);
- if ((end > DEFAULT_BUFFER_SIZE) && (end >= position) &&
- (position >= 0)) {
- return currentSize + (end - position) + 1;
- }
- } except OsError {}
- if (currentSize <= 0) {
- return DEFAULT_BUFFER_SIZE;
- }
- return currentSize + (currentSize >> 2) + 32;
- }
- function
- _raiseModeError (
- mode
- )
- /*++
- Routine Description:
- This routine raises an error for an operation the file is not open for.
- Arguments:
- mode - Supplies the mode string to print in the error.
- Return Value:
- None.
- --*/
- {
- Core.raise(ValueError("File not open for " + mode));
- }
- function
- _raiseIoError (
- oserror
- )
- /*++
- Routine Description:
- This routine raises an I/O error from an OS error.
- Arguments:
- oserror - Supplies the OS error.
- Return Value:
- None.
- --*/
- {
- var ioerror = IoError(oserror.args);
- ioerror.errno = oserror.errno;
- Core.raise(ioerror);
- return;
- }
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
|