# Filesystem APIs

### Filesystem APIs

The standard library gives access to files and directories through `std.fs`.

A small program can open a file and read its contents.

```zig
const std = @import("std");

pub fn main() !void {
    const file = try std.fs.cwd().openFile("notes.txt", .{});
    defer file.close();

    var buffer: [1024]u8 = undefined;

    const n = try file.readAll(buffer[0..]);

    std.debug.print("{s}", .{buffer[0..n]});
}
```

`std.fs.cwd()` returns a handle to the current working directory.

```zig
std.fs.cwd()
```

A directory handle is used to open files relative to that directory.

```zig
openFile("notes.txt", .{})
```

The second argument is an options struct. Here it is empty, so the defaults are used.

Opening a file can fail.

The file may not exist.

The process may not have permission.

The path may name a directory instead of a file.

For this reason, `openFile` returns an error union, and the call uses `try`.

```zig
const file = try std.fs.cwd().openFile("notes.txt", .{});
```

The file must be closed when the program is finished with it.

```zig
defer file.close();
```

The `defer` statement makes the close happen when `main` returns.

Reading into a fixed buffer is explicit.

```zig
var buffer: [1024]u8 = undefined;
```

The buffer is uninitialized. The file read fills part of it.

```zig
const n = try file.readAll(buffer[0..]);
```

`readAll` reads bytes into the slice and returns the number of bytes read.

Only the first `n` bytes contain file data.

```zig
buffer[0..n]
```

Writing a file is similar.

```zig
const std = @import("std");

pub fn main() !void {
    const file = try std.fs.cwd().createFile("out.txt", .{});
    defer file.close();

    try file.writeAll("hello\n");
}
```

`createFile` creates or truncates a file.

`writeAll` writes the whole slice or returns an error.

For small files, the standard library can allocate a buffer and read the whole file.

```zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    const allocator = gpa.allocator();

    const data = try std.fs.cwd().readFileAlloc(
        allocator,
        "notes.txt",
        1024 * 1024,
    );
    defer allocator.free(data);

    std.debug.print("{s}", .{data});
}
```

The last argument is the maximum number of bytes to read.

```zig
1024 * 1024
```

This limit matters. A program should not accidentally allocate memory for an enormous file.

The returned slice is owned by the caller.

```zig
defer allocator.free(data);
```

Directory operations also begin from a directory handle.

```zig
const std = @import("std");

pub fn main() !void {
    try std.fs.cwd().makeDir("tmp");
}
```

This creates a directory named `tmp` in the current working directory.

To delete an empty directory:

```zig
try std.fs.cwd().deleteDir("tmp");
```

To delete a file:

```zig
try std.fs.cwd().deleteFile("out.txt");
```

Paths are passed as byte slices.

```zig
[]const u8
```

The interpretation of paths depends on the operating system. Zig exposes filesystem operations without pretending that every platform is identical.

Iterating over a directory uses an opened iterable directory.

```zig
const std = @import("std");

pub fn main() !void {
    var dir = try std.fs.cwd().openDir(".", .{ .iterate = true });
    defer dir.close();

    var it = dir.iterate();

    while (try it.next()) |entry| {
        std.debug.print("{s}\n", .{entry.name});
    }
}
```

The entry contains a name and a kind.

```zig
entry.name
entry.kind
```

The name is relative to the directory being iterated.

The kind tells whether the entry is a file, directory, symbolic link, or something else.

A common program pattern is:

```zig
const dir = std.fs.cwd();
```

Then use `dir` for all file operations.

This keeps paths relative and makes code easier to test.

For programs that need absolute paths, use the appropriate functions in `std.fs` and `std.process`.

Filesystem code usually has many possible errors. Zig makes these errors part of the type of the operation.

A file operation can fail because the file is missing, a directory is missing, permissions are wrong, storage is full, or the operating system rejects the path.

The code does not need exceptions to express this.

It uses error unions.

```zig
try file.writeAll(data);
```

This is direct and visible.

Exercise 14-21. Create a file named `hello.txt` and write one line to it.

Exercise 14-22. Read `hello.txt` into a fixed buffer and print it.

Exercise 14-23. Read a file with `readFileAlloc` and free the returned memory.

Exercise 14-24. Iterate over the current directory and print each entry name.

