# A File Copier

### A File Copier

A file copier is a useful small program. It opens one file for reading, opens another file for writing, then copies bytes from the first to the second.

The command will look like this:

```text
copy source.txt output.txt
```

Here is a complete first version.

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

pub fn main() !void {
    var args = std.process.args();

    _ = args.next();

    const src_path = args.next() orelse {
        std.debug.print("missing source file\n", .{});
        return;
    };

    const dst_path = args.next() orelse {
        std.debug.print("missing destination file\n", .{});
        return;
    };

    const cwd = std.fs.cwd();

    var src = try cwd.openFile(src_path, .{});
    defer src.close();

    var dst = try cwd.createFile(dst_path, .{});
    defer dst.close();

    var buffer: [4096]u8 = undefined;

    while (true) {
        const n = try src.read(&buffer);
        if (n == 0) break;

        try dst.writeAll(buffer[0..n]);
    }
}
```

Run it:

```sh
zig run main.zig -- source.txt output.txt
```

The program starts by reading arguments. The first argument is the executable path and is ignored.

```zig
_ = args.next();
```

The next two arguments are file names.

```zig
const src_path = args.next() orelse {
    std.debug.print("missing source file\n", .{});
    return;
};

const dst_path = args.next() orelse {
    std.debug.print("missing destination file\n", .{});
    return;
};
```

Each call to `args.next()` returns an optional byte slice. If the argument is missing, the program prints an error and returns.

The current working directory is obtained with:

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

This gives a directory handle. File operations are done relative to it.

The source file is opened for reading:

```zig
var src = try cwd.openFile(src_path, .{});
defer src.close();
```

The destination file is created for writing:

```zig
var dst = try cwd.createFile(dst_path, .{});
defer dst.close();
```

`defer` runs when the current scope exits. It is a good fit for closing files. The file is closed whether the copy succeeds or an error occurs.

The buffer is an array of 4096 bytes.

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

`undefined` means the bytes are not initialized. That is fine here because the program fills the buffer before reading from it.

The copy loop reads bytes into the buffer.

```zig
const n = try src.read(&buffer);
```

`read` returns the number of bytes actually read. At the end of the file it returns zero.

```zig
if (n == 0) break;
```

Only the part of the buffer that contains data is written.

```zig
try dst.writeAll(buffer[0..n]);
```

The expression `buffer[0..n]` is a slice. It refers to the first `n` bytes of the array.

`writeAll` keeps writing until the whole slice has been written or an error occurs.

A file copier shows several common Zig ideas at once. Resources are explicit. Errors are returned. Cleanup is local. The buffer is ordinary memory, not a hidden object.

A shorter version can use the standard library helper.

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

pub fn main() !void {
    var args = std.process.args();

    _ = args.next();

    const src_path = args.next() orelse return error.MissingSource;
    const dst_path = args.next() orelse return error.MissingDestination;

    const cwd = std.fs.cwd();

    var src = try cwd.openFile(src_path, .{});
    defer src.close();

    var dst = try cwd.createFile(dst_path, .{});
    defer dst.close();

    try src.copyRangeAll(0, dst, 0, try src.getEndPos());
}
```

This version copies the range from offset zero to the end of the file. It is compact, but the explicit loop is worth understanding first. Most I/O programs are variations of that loop.

Exercise 20-6. Change the buffer size to 16 KiB and verify that the program still works.

Exercise 20-7. Refuse to overwrite the destination file if it already exists.

Exercise 20-8. Print the number of bytes copied.

Exercise 20-9. Copy from standard input when the source path is `-`.

Exercise 20-10. Copy to standard output when the destination path is `-`.

