Skip to content

Tiri IO API

Paul Manias edited this page Feb 9, 2026 · 3 revisions

The I/O interface provides helper functions for file management and user interactivity. The majority of the functionality is built-in, but UI extensions and file searching are optional, requiring use of the import keyword to load them first.

It should also be noted that while the io interfaces are largely backwards compatible with their Lua counterparts, a key difference is that filesystem failures will throw exceptions to make it easier to capture errors.

The io interface is designed to provide basic filesystem services only. For more advanced filesystem functionality, the Core module and File class provide extensive features that are cross-platform compatible.

Built-In Functions

io.open()

file = io.open(Path, [Mode])

Opens the file at Path, requesting access permissions specified by Mode. If Mode is not set, the file is opened in read-only mode. If an error occurs, an exception is thrown with a descriptive error message.

file = io.open('temp:myfile.txt', 'w')

Available options for Mode are as follows:

Option Description
r Read only.
w Create new file in read/write mode.
a Append to existing file in read/write mode. Automatically creates file if it does not exist.
+ Open existing file in read/write mode.

io.close()

io.close([File])

Closes the given file handle. If no File is provided, the default output file is closed. Returns true on success.

file = io.open('temp:myfile.txt', 'r')
io.close(file)

io.read()

data1, data2, ... = io.read([Format, ...])

Reads from the default input file according to the given format strings. If no format is provided, a single line is read. The default input file is initially stdin but can be changed with io.input().

See file:read() for a full description of format strings.

line = io.read()            -- Read one line
num  = io.read('*n')        -- Read a number
all  = io.read('*a')        -- Read entire file

io.write()

io.write(Data, ...)

Writes each argument to the default output file. The default output file is initially stdout but can be changed with io.output(). Arguments must be strings or numbers.

io.write('Hello, ')
io.write('world!\n')

io.flush()

io.flush()

Flushes the default output file, ensuring that any buffered data is written to disk.

io.input()

file = io.input([Source])

When called with no arguments, returns the current default input file handle. When called with a string, opens the named file in read mode and sets it as the default input. When called with a file handle, sets that handle as the default input.

The default input is initially stdin.

io.input('temp:data.txt')       -- Set default input to a file
line = io.read()                -- Read a line from that file
original = io.input()           -- Retrieve the current default input

io.output()

file = io.output([Source])

When called with no arguments, returns the current default output file handle. When called with a string, opens the named file in write mode and sets it as the default output. When called with a file handle, sets that handle as the default output.

The default output is initially stdout.

io.output('temp:results.txt')   -- Set default output to a file
io.write('Result: 42\n')        -- Write to that file

io.lines()

iterator = io.lines([Source])

Returns an iterator function that reads lines from a file. When called with a filename, opens the file and closes it automatically when iteration completes or the iterator is garbage collected. When called with a file handle, iterates lines without closing the handle. When called with no arguments, iterates lines from the default input.

for line in io.lines('temp:data.txt') do
   print(line)
end

io.type()

result = io.type(Value)

Tests whether Value is a valid file handle. Returns "file" if Value is an open file handle, "closed file" if it is a closed file handle, or nil if it is not a file handle.

file = io.open('temp:test.txt', 'w')
print(io.type(file))    -- "file"
file:close()
print(io.type(file))    -- "closed file"
print(io.type('hello')) -- nil

io.tempFile()

file = io.tempFile()

Creates a temporary in-memory buffer file opened for both reading and writing. The buffer is backed by system memory, allowing the OS to use swap space for large files. The file is automatically freed when the handle is garbage collected or explicitly closed.

tmp = io.tempFile()
tmp:write('temporary data')
tmp:seek('set', 0)
print(tmp:read('*a'))   -- "temporary data"
tmp:close()

io.readAll()

content = io.readAll(Path)

Reads the entire contents of the file at Path and returns it as a string. This is a convenience function that opens the file, reads all data, and closes it in a single operation. Throws an exception if the file cannot be opened or read.

content = io.readAll('config:settings.tiri')

io.writeAll()

io.writeAll(Path, Content)

Writes Content to the file at Path, creating the file if it does not exist or overwriting it if it does. Throws an exception if the file cannot be created or written to.

io.writeAll('temp:output.txt', 'Hello, world!\n')

io.isFolder()

bool = io.isFolder(Path)

Returns true if Path ends with a folder separator character (/, \ or :), indicating that the path refers to a directory rather than a file.

print(io.isFolder('system:users/'))   -- true
print(io.isFolder('temp:file.txt'))   -- false

io.splitPath()

dir, file = io.splitPath(Path)

Splits Path into its directory and filename components. The directory portion includes the trailing separator. Returns nil, nil if Path is nil or empty. Returns nil, Path if no separator is found.

dir, file = io.splitPath('system:users/readme.txt')
-- dir  = "system:users/"
-- file = "readme.txt"

dir, file = io.splitPath('readme.txt')
-- dir  = nil
-- file = "readme.txt"

io.sanitisePath()

cleaned = io.sanitisePath(Path)

Removes repeated consecutive path separator characters (/, \, :) from Path. Useful for cleaning up paths constructed by concatenation.

print(io.sanitisePath('system://users///file.txt'))  -- "system:/users/file.txt"

File Handle Methods

File handles returned by io.open() and related functions support the following methods, called with the colon syntax (e.g. file:read()).

file:read()

data1, data2, ... = file:read([Format, ...])

Reads from the file according to the given format strings. Multiple formats can be provided and each returns a separate value. If no format is provided, a single line is read.

Format Description
*l Read a single line, stripping the newline character. This is the default.
*a Read the entire remaining contents of the file.
*n Read a line and convert it to a number.
(number) Read the specified number of bytes.
file = io.open('temp:data.txt', 'r')
line = file:read()          -- Read one line
file:seek('set', 0)
all  = file:read('*a')      -- Read entire file
file:close()

file:write()

file = file:write(Data, ...)

Writes each argument to the file. Arguments must be strings or numbers. Returns the file handle to allow chained calls.

file = io.open('temp:output.txt', 'w')
file:write('Line 1\n'):write('Line 2\n')
file:close()

file:close()

file:close()

Closes the file handle and releases the underlying file object. Returns true on success.

file:flush()

file:flush()

Flushes any buffered output data to disk, ensuring all writes are committed. Returns true on success.

file:seek()

position = file:seek([Whence, [Offset]])

Sets the file position for reading and writing. Returns the resulting absolute file position.

Whence Description
set Position relative to the beginning of the file.
cur Position relative to the current location. This is the default.
end Position relative to the end of the file.

Offset defaults to 0.

file = io.open('temp:data.txt', 'r')
file:seek('set', 10)     -- Move to byte 10
pos = file:seek('cur')   -- Get current position
file:seek('end', 0)      -- Move to end of file
file:close()

file:lines()

iterator = file:lines()

Returns an iterator function that reads lines from the file. The file handle is not closed when iteration completes, unlike io.lines() with a filename argument.

file = io.open('temp:data.txt', 'r')
for line in file:lines() do
   print(line)
end
file:close()

I/O Extensions

File Search

The FileSearch interface simplifies the process of searching for files and folders within the file system. Support is provided for both wildcard filters and content searching.

It can be loaded with the line:

import 'io/filesearch'

Functions

io.search()

io.search(Path | {PathA, PathB, ...}, Options)

This example searches for text files in the documents: folder that include the word tennis:

   import 'io/filesearch'

io.search('documents:', {
   nameFilter    = regex.new('\\.txt$'),
   contentFilter = 'tennis',
   maxDepth      = 2,  -- Only search 2 levels deep
   matchFeedback = function(Path, FileName, File)
      print(Path, ' ', FileName)
   end
})

Valid options to use when defining the search table are as follows:

Option Description
nameFilter Include file/folder names that match this filter string exactly, or provide a compiled regex for pattern matching.
contentFilter Include files that contain this content.
caseSensitive Enforces case-sensitive name comparisons if true.
matchFeedback Call this function each time that a matched file is discovered. Returning ERR_Terminate will stop the search. Synopsis: function(Path, File, Attributes)
ignoreFeedback Call this function each time that a file fails to pass filter matches.
minSize Filter out files that do not meet or exceed this minimum size.
maxSize Filter out files that exceed this size.
minDate Filter out files with a modification time less than this timestamp.
maxDate Filter out files with a modification time exceeding this timestamp.
scanLinks Allows symbolically linked files and folders to be included in the file search.
matchFolders Include matching folder names in the output of the search.
ignoreFiles Do not scan files (useful only in conjunction with matchFolders).
maxDepth Maximum depth to recurse into subdirectories (0 = current directory only, 1 = one level deep, etc.).

When searching files for content, it is strongly recommended that a filter or size limit is applied so that only a limited number of files are opened for searching.

File Manager

The File Manager API supports common file management needs such as mass file transfer and clipboard operations, and adds integrated UI support so that user interactions are managed in a consistent way across all Kōtuku applications.

For example, copying a large number of files will pop-up a progress dialog to keep the user informed of progress. If an operation will overwrite an existing file, the user will be notified and given a choice of whether or not to continue.

It can be loaded with the line:

import 'io/filemgr'

Functions

io.ui.copy()

error = io.ui.copy(Source, Dest, Move)

Copy the Source path or paths (if provided as a list of strings) to the file or folder indicated by Dest.

If Move is true then each file or folder copied from Source will be deleted when the copy is successful.

Any errors that occur will be brought to the user's attention with a dialog box. Exceptions will only be thrown if there a catastrophic failure during the copy process. Otherwise, assume that the resulting error was handled and should only be used as an indicator of the final result. If the user cancels the operation then ERR_Cancelled is returned.

io.ui.paste()

io.ui.paste(Dest)

Paste files from the clipboard to the destination path. The same behavioural rules for io.ui.copy apply.

io.ui.delete()

io.ui.delete(Paths)

Delete the identified file or folder at Paths, which may be a string or list of strings. A progress window is opened if the operation takes longer than expected to run.

Clone this wiki locally