Dealing with Files


Alusus Standard Runtime Library includes `Fs` module (stands for Fyle System) that contains functions for dealing with the file system. It allows you to do these operations:

  1. Create a new file.
  2. Open an existing file.
  3. Read from a file.
  4. Write to a file.
  5. Delete a file.
  6. List files from a directory.

Opening a File


When dealing with file we should define a pointer with type `File` to deal with the file using it, and as mentioned at the start of this lesson, we should include `Fs` module.

import "Srl/Fs.alusus";
def fp: ptr[Fs.File];

After defining a pointer with type `File`, to deal with the file we should open it, so we will use `openFile` function and determine whether we want to only read from the file, only writing to it, or the both.

fp = openFile("path/to/file", "r");

This function returns a pointer to the required file, and it will stored in the pointer `fp`. The first argument is a string containing the file name with its path, whereas the second argument determines the mode we will open the file with. In what follows are the available options for the mode (in this example the file is opened in read-only mode):

  1. "r"read-only.
  2. "w" write-only (it will create the file if it does not exist).
  3. "a" append a new data to the end of the file (start writing from the end of the file).
  4. "+r" read and write
  5. "+w" read and write (it will create the file if it does not exist).
  6. "+a" read and update (it will create the file if it does not exist). it will start reading from the start but the writing is done at the end.

After that you could handle the file as you want using the appropriate functions. Usually, you will need to functions like `read` to read the file, `write` to write the file, `seek` to move inside the file while reading or writing, and `tell` to get the current position in the file.

Writing a File


We use `write` function to write to files.

write(content, contentSize, contentCount, fp);

This function writes the provided content to the open file. It returns an integer representing the number of content items written to the file. If no error occurrs while writing to the file it returns a result equal to `contentCount`.

The first argument to the function is a pointer to the content array we want to write. Whereas the second argument is the size of each element in the content array. The third argument is the length of the content array. Finally, the last argument is the pointer to the file that we get using `openFile` function.

Let's try writing a text to a file:

def text:String;
text= "Alusus Language";

`text` variable with type `String` internally is a pointer to an array of chars, so we can pass it directly to `write` function as a first argument. Now each item from this array is a char its size is 1 so the size of the the contents is ` and since the size of different types may be different from architecture to another we will put `Char~size` as a second argument which is equal to 1 on linux. Regarding the number of contents, it is the number if items that will be written into the file and since the string is an array of chars, the number of contents is the length of the string so we can use `getLength` function, and finally we pass the required pointer to the file we want to write to.

Fs.write(text, char~size, text.getLength() + 1, fp);

We added 1 to the result of `getLength` to account for the `\0` character at the end of the string, which gets added automatically by String but isn't counted by `getLength'.

Closing a File


Now that we are done writing to the file, we must close it using `closeFile` function, which takes only a single argument that is the pointer to the file.

Fs.closeFile(fp);

The full code:

import "Srl/Console.alusus";
import "Srl/Fs.alusus";
import "Srl/String.alusus";
use Srl;
def path: String;
def text: String;
def fp: ptr[Fs.File];
path = "/home/ali/textfile.txt"; // file path.
text = "Alusus Language"; // string we will write it to the file.
fp = Fs.openFile(path, "w"); // opening the file for writing.
Fs.write(text, char~size, text.getLength() + 1, fp); // writing the text to the file.
Fs.closeFile(fp);

Read a File Using `read` Function


Now after we write to the file, we will write another code to read its contents, so first we will open the file but in read-only mode, then we will use `read` function to read its contents.

readCount = read(content~ptr, contentSize, maxContentCount, fp);

The arguments look similar to those of the `write` function, but the provided buffer will be written to instead of read from. In order to allocate enough buffer to hold the entire file we will need to know the size of the file, and this can be done using `tell` and `seek` functions. We will use `seek` tim move to the end of the file, and this function takes 3 arguments.

Fs.seek(fp, 0, Fs.Seek.END);

In the above function we asked to move the offset 0 from the end of the file, which means we asked to move to the end of the file. The last argument could be any of the following values:

  • 0 or `Seek.SET`: change to change the cursor position to the value of second argument in bytes, starting from the file beginning. Which means it moves the cursor directly to the second argument, if it is 1 it will move to the first character, if it is 0 it will move to the file beginning.
  • 1 or `Seek.CUR`: move the cursor from the current position by the value of the second argument in bytes. In other words, it moves the cursor forward by the value of the second argument. So if the second argument is 1 it will moves forward by 1 character and if it is -1 it will move backwards by 1 character.
  • 2 or `Seek.END`: It is the same as first one but from the end of the file instead of its beginning.

Now after we reached the end of the file we can know the size of the file using `tell` function which tells us the position we are at, and this equal to the number of bytes from the beginning of the file, which is the size of the file.

totalCount = Fs.tell(fp);

After we knew the size of the file we can allocate enough memory to hold its contents, so we will use `alloc` function defined in `String` class to do that:

text.alloc(totalCount);

Go the beginning of the file:

Fs.seek(fp, 0, Fs.Seek.SET);

Call `read` function as follows:

Fs.read(text.buf, char~size, totalCount, fp);

The full code:

import "Srl/Console.alusus";
import "Srl/Fs.alusus";
import "Srl/String.alusus";
use Srl;
def path: String;
def text: String;
def count: ArchInt;
def fp: ptr[Fs.File];
path = "/home/ali/textfile.txt";
fp = Fs.openFile(path, "r"); // open the file for reading
Fs.seek(fp, 0, Fs.Seek.END); // move to the end of the file
count = Fs.tell(fp); // get the current position
Fs.seek(fp, 0, Fs.Seek.SET); // go back to the beginning of the file
text.alloc(count); // allocate enough memory for the file contents
Fs.read(text,char~size, count, fp); // read from the file
Console.print("%s", text.buf) // print the file contents
Fs.closeFile(fp) // close the file

Read a File Using `readFile` Function


The `readFile` function reads the entire file at once after allocating enough memory for it, and it returns the allocated memory buffer, which the user must eventually free.

readFile (
  filename: ptr[array[Char]], result: ptr[ptr], size: ptr[ArchInt]
): Bool;

It returns 1 if the read succeeds, 0 otherwise.

`filename`: is the name or the path of the file to read.
`result`: is a pointer to a pointer. The function automatically allocates the memory and read the file contents, and it allows you to access the contents of the file using it.
`size`: is a pointer to a variable with type `ArchInt` which the function write in it the size of the file it read.

We will now read the previous file using `readFile` function:

import "Srl/Console.alusus";
import "Srl/Fs.alusus";
import "Srl/String.alusus";
use Srl;
def path: String = "/home/ali/textfile.txt";
def result: ptr[Char];
def count: ArchInt; // define a variable to hold the number of bytes read.
Fs.readFile(path, result~ptr, count~ptr); // read the file contents
Console.print(result);
Memory.free(result);