# Directories and Paths

### Directories and Paths

A file lives inside a directory.

A directory is a container for file names and other directories. Many systems call directories “folders,” but in programming, “directory” is the more common term.

A path is a name that tells the operating system where something is.

For example:

```text
hello.txt
```

is a path.

```text
src/main.zig
```

is also a path.

```text
/home/alice/project/src/main.zig
```

is another path.

When your program reads or writes files, it almost always works with paths and directories.

#### Current Working Directory

Every running program has a current working directory.

This is the directory that relative paths are resolved from.

If your program uses this path:

```text
hello.txt
```

the operating system interprets it relative to the current working directory.

So if your program is running inside:

```text
/home/alice/project
```

then:

```text
hello.txt
```

means:

```text
/home/alice/project/hello.txt
```

In Zig 0.16 style, you can refer to the current working directory through `std.Io.Dir.cwd()` when using the newer I/O APIs. Zig 0.16.0 is the current latest official release, and its release notes describe the standard library’s newer `std.Io` work as one of the major changes.

A typical file operation starts like this:

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

pub fn main(init: std.process.Init) !void {
    const io = init.io;

    const file = try std.Io.Dir.cwd().openFile(io, "hello.txt", .{});
    defer file.close(io);

    // use file here
}
```

This means: open `hello.txt` from the current working directory.

#### Relative Paths

A relative path is interpreted from some starting directory.

These are relative paths:

```text
hello.txt
src/main.zig
../notes.txt
./data/input.txt
```

The path:

```text
src/main.zig
```

means: go into the `src` directory, then find `main.zig`.

The path:

```text
../notes.txt
```

means: go to the parent directory, then find `notes.txt`.

The path:

```text
./data/input.txt
```

means: start here, go into `data`, then find `input.txt`.

The dot `.` means the current directory.

The double dot `..` means the parent directory.

Relative paths are useful for project files, test data, configuration files, and command-line tools.

#### Absolute Paths

An absolute path starts from a system root.

On Linux and macOS, an absolute path usually starts with `/`:

```text
/home/alice/project/hello.txt
```

On Windows, an absolute path may look like this:

```text
C:\Users\Alice\project\hello.txt
```

Absolute paths are useful when you need an exact location.

But many programs should avoid hard-coding absolute paths. They make programs less portable.

This is fragile:

```text
/home/alice/project/data.txt
```

It works only on Alice’s machine, in that exact directory.

This is usually better:

```text
data.txt
```

or:

```text
data/input.txt
```

The program can then run from the project directory on different machines.

#### Path Separators

Different operating systems write paths differently.

Unix-like systems use `/`:

```text
src/main.zig
```

Windows commonly uses `\`:

```text
src\main.zig
```

Zig’s standard library has path utilities to help with platform differences. For simple examples, you will often see `/` used in paths because Zig can handle many common cases, but real cross-platform programs should avoid manually stitching paths with string concatenation.

This is suspicious:

```zig
const path = "data/" ++ filename;
```

It assumes `/`.

A safer design is to use standard library path helpers when building paths dynamically.

#### Opening a Directory

A directory can be opened much like a file.

You open a directory when you want to work relative to that directory, list entries inside it, or create files inside it.

The current working directory is already available:

```zig
const cwd = std.Io.Dir.cwd();
```

Then you can open files relative to it:

```zig
const file = try cwd.openFile(io, "hello.txt", .{});
defer file.close(io);
```

This style is useful because the directory becomes the base for later operations.

Instead of thinking in raw strings, think in two parts:

the directory you start from

the relative path inside that directory

That is often safer and clearer.

#### Creating a Directory

Many programs need to create directories.

For example, a command-line tool might create an output directory:

```text
build-output
```

The shape of the code is:

```zig
try std.Io.Dir.cwd().makeDir(io, "build-output");
```

Directory creation can fail.

The directory might already exist.

The parent path might not exist.

The program might not have permission.

The disk might be read-only.

So directory creation uses `try`.

A practical program often handles “already exists” separately:

```zig
std.Io.Dir.cwd().makeDir(io, "build-output") catch |err| switch (err) {
    error.PathAlreadyExists => {},
    else => return err,
};
```

This says: if the directory already exists, that is fine. For every other error, return the error.

This is a common Zig pattern.

#### Creating a File Inside a Directory

Once you have a directory, you can create a file inside it.

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

pub fn main(init: std.process.Init) !void {
    const io = init.io;

    const cwd = std.Io.Dir.cwd();

    cwd.makeDir(io, "out") catch |err| switch (err) {
        error.PathAlreadyExists => {},
        else => return err,
    };

    const file = try cwd.createFile(io, "out/result.txt", .{});
    defer file.close(io);

    try file.writeAll(io, "created inside out\n");
}
```

This program creates a directory named `out`, then creates:

```text
out/result.txt
```

The important point is that `out/result.txt` is still a relative path. It is relative to `cwd`.

#### Listing Directory Entries

A directory contains entries.

An entry can be a file, a directory, a symbolic link, or something else depending on the operating system.

The exact iterator API can vary across Zig versions, so the safest source for your installed compiler is:

```bash
zig std
```

Conceptually, directory listing looks like this:

```zig
const dir = try std.Io.Dir.cwd().openDir(io, "src", .{
    .iterate = true,
});
defer dir.close(io);

var iterator = dir.iterate(io);

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

The structure is more important than the exact syntax.

Open the directory.

Ask for iteration support.

Create an iterator.

Call `next` until it returns no more entries.

Use each entry name.

Directory iteration can fail because the file system can change while you are reading it. A directory can be removed. Permissions can change. A disk can fail. Network file systems can disconnect.

That is why `next` may need `try`.

#### Entry Names Are Not Full Paths

When you list a directory, each entry usually gives you a name, not a full path.

If you are listing:

```text
src
```

and the entry name is:

```text
main.zig
```

the full relative path is:

```text
src/main.zig
```

Do not confuse the two.

Entry name:

```text
main.zig
```

Path from project root:

```text
src/main.zig
```

Absolute path:

```text
/home/alice/project/src/main.zig
```

These are different pieces of information.

#### Joining Paths

Suppose you have a directory name and a file name:

```zig
const dir_name = "src";
const file_name = "main.zig";
```

You want:

```text
src/main.zig
```

For quick examples, you might write the path directly. For real code, path joining should use standard library helpers because path rules vary by operating system.

The conceptual operation is:

```zig
const path = join(dir_name, file_name);
```

The result is a valid path for the target platform.

When path construction needs memory, the function may require an allocator. This follows Zig’s rule: allocation should be visible.

A common shape is:

```zig
const path = try std.fs.path.join(allocator, &.{ "src", "main.zig" });
defer allocator.free(path);
```

This creates a joined path and later frees it.

The exact namespace and APIs may differ depending on the Zig version and whether you are using older `std.fs` APIs or newer `std.Io` APIs. The principle stays the same: do not build nontrivial paths by careless string concatenation.

#### Checking File Metadata

Sometimes you need information about a file or directory.

That information is called metadata.

Metadata may include:

file size

file kind

permissions

modification time

A common operation is `stat`.

For a file:

```zig
const stat = try file.stat(io);
std.debug.print("size = {}\n", .{stat.size});
```

For a path, a directory API may also provide ways to stat an entry.

This is useful when you need to know whether something is a file or directory before processing it.

But be careful: file systems can change between checking and using. A file might exist when you check it and be gone one moment later.

So code still needs error handling at the point of use.

#### Deleting Files

Deleting a file removes a directory entry.

The conceptual operation is:

```zig
try std.Io.Dir.cwd().deleteFile(io, "old.txt");
```

This can fail.

The file might not exist.

The path might be a directory.

The program might not have permission.

The file might be locked by another process.

For command-line tools, you may want to handle missing files gracefully:

```zig
std.Io.Dir.cwd().deleteFile(io, "old.txt") catch |err| switch (err) {
    error.FileNotFound => {},
    else => return err,
};
```

This says: if the file is already gone, that is acceptable.

#### Removing Directories

Removing a directory is different from deleting a file.

A directory may need to be empty before it can be removed.

The conceptual operation is:

```zig
try std.Io.Dir.cwd().deleteDir(io, "empty-dir");
```

If the directory contains files, this may fail.

Recursive deletion is more dangerous because it removes a whole tree. Use it carefully. Bugs in recursive deletion can destroy data quickly.

For beginner code, prefer simple deletion first.

#### Paths Are Data from Outside Your Program

Paths often come from users.

For example:

```bash
mytool input.txt
```

Here, `input.txt` is user input.

Treat paths carefully.

A user might pass:

```text
../../important-file
```

or an absolute path:

```text
/etc/passwd
```

or a path with unusual characters.

This matters especially for servers, archive extractors, package managers, build tools, and programs that write files.

Do not blindly join untrusted paths and write to them.

A safe program should decide which directories it is allowed to access, then validate or normalize paths before using them.

#### A Small Directory Listing Program

Here is a complete beginner-style example:

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

pub fn main(init: std.process.Init) !void {
    const io = init.io;

    const dir = try std.Io.Dir.cwd().openDir(io, ".", .{
        .iterate = true,
    });
    defer dir.close(io);

    var iterator = dir.iterate(io);

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

This lists entries in the current directory.

The path `"."` means “this directory.”

The option `.iterate = true` means we want to iterate through entries.

The loop keeps asking for the next entry until there are no more.

#### A Small “Create Output File” Program

This example creates an output directory, then writes a file inside it:

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

pub fn main(init: std.process.Init) !void {
    const io = init.io;

    const cwd = std.Io.Dir.cwd();

    cwd.makeDir(io, "out") catch |err| switch (err) {
        error.PathAlreadyExists => {},
        else => return err,
    };

    const file = try cwd.createFile(io, "out/message.txt", .{
        .truncate = true,
    });
    defer file.close(io);

    try file.writeAll(io, "hello from Zig\n");
}
```

After running it, the project directory contains:

```text
out/message.txt
```

and the file contains:

```text
hello from Zig
```

#### Directory APIs Teach a Larger Zig Habit

Directory and path code teaches a larger habit in Zig: be explicit about the base of an operation.

Instead of passing loose path strings everywhere, think about which directory owns the operation.

For example:

```zig
const cwd = std.Io.Dir.cwd();
```

Then operations are relative to that directory:

```zig
try cwd.createFile(io, "out/message.txt", .{});
```

This helps you reason about file system effects.

Where can this program read?

Where can this program write?

Which paths are relative?

Which paths are absolute?

Which operation may fail?

Zig does not remove these questions. It makes them visible.

#### What You Should Remember

A directory contains files and other directories.

A path names a file system location.

A relative path depends on a starting directory.

An absolute path starts from the system root.

The current working directory is the default base for many relative paths.

Use directory APIs to open, create, list, and delete entries.

Use `defer` to close opened directories and files.

Use path helpers instead of careless string concatenation.

Treat user-provided paths carefully.

File systems can change while your program runs, so always handle errors at the point where you open, read, write, create, or delete.

