Zig - [Shell] Stage #JV1 (Redirection - Redirect stdout)

I’m stuck on Stage #(Redirection - Redirect stdout, ex. #JV1).

Here are my logs:

remote: [tester::#JV1] Running tests for Stage #JV1 (Redirection - Redirect stdout)
remote: [tester::#JV1] Running ./your_program.sh
remote: [tester::#JV1] Writing file "/tmp/qux/mango" with content "mango"
remote: [tester::#JV1] Writing file "/tmp/qux/orange" with content "orange"
remote: [tester::#JV1] Writing file "/tmp/qux/pineapple" with content "pineapple"
remote: [your-program] $ ls /tmp/qux > /tmp/foo/bar.md
remote: [your-program] $ cat /tmp/foo/bar.md
remote: [your-program] mango
remote: [your-program] orange
remote: [your-program] pineapple
remote: [tester::#JV1] ✓ Received redirected file content
remote: [your-program] $ echo 'Hello Emily' 1> /tmp/foo/baz.md
remote: [your-program] $ cat /tmp/foo/baz.md
remote: [your-program] Hello Emily
remote: [tester::#JV1] ✓ Received redirected file content
remote: [your-program] $ cat /tmp/qux/orange nonexistent 1> /tmp/foo/qux.md
remote: [your-program] cat: nonexistent: No such file or directory
remote: [tester::#JV1] ✓ Received error message
remote: [your-program] $ cat /tmp/foo/qux.md
remote: [your-program] /tmp/foo/qux.md does not exist
remote: [tester::#JV1] Output does not match expected value.
remote: [tester::#JV1] Expected: "orange"
remote: [tester::#JV1] Received: "/tmp/foo/qux.md does not exist" // that's a call from me to see if that file exists on your machine while running the tests
remote: [your-program] cat: /tmp/foo/qux.md: No such file or directory
remote: [your-program] $
remote: [tester::#JV1] Assertion failed.
remote: [tester::#JV1] Test failed

And here’s a snippet of my code:

// That's my cat fn.
fn cat(allocator: std.mem.Allocator, cmd: []const u8, writer: std.fs.File.Writer) !std.ArrayList([]const u8) {
    const file_paths = std.mem.trim(u8, cmd[3..], " ");
    const delimiters: []const u8 = if (isSingleQuote(file_paths)) "'" else if (isDoubleQuote(file_paths)) "\"" else " ";

    var concatenated = std.ArrayList([]const u8).init(allocator);

    var it = std.mem.tokenizeAny(u8, file_paths, delimiters);
    while (it.next()) |file_path| {
        if (!std.mem.eql(u8, file_path, "nonexistent") and !fileExists(file_path)) {
            try writer.print("{s} does not exist\n", .{file_path});
        }
        const file_contents = listFile(allocator, file_path) catch |e| switch (e) {
            error.IsDir => {
                // try writer.print("1 listFile error={s}\n", .{@errorName(e)});

                const sep: []const u8 = &.{getSystemLineSep()};
                const maybe_backslash_idx = std.mem.lastIndexOf(u8, file_path, sep);

                // try writer.print("getSystemLineSep={s}\n", .{sep});

                if (maybe_backslash_idx) |backslash_idx| {
                    const sub_folder_path = file_path[0..backslash_idx];

                    // try writer.print("sub_folder_path={s}\n", .{sub_folder_path});

                    const file_content = try listFile(allocator, sub_folder_path);
                    try concatenated.append(file_content);

                    continue;
                } else {
                    try writer.print("cat: {s}: No such file or directory\n", .{file_path});
                    return;
                }
            },
            else => {
                // try writer.print("1 listFile error={s}\n", .{@errorName(e)});
                try writer.print("cat: {s}: No such file or directory\n", .{file_path});
                return;
            },
        };

        try concatenated.append(file_contents);
    }

    return concatenated;
}

// That's how I check if file exists or not:

fn fileExists(path: []const u8) bool {
    std.fs.cwd().access(path, .{}) catch return false;
    return true;
}

I have tried the real ‘cat’ implementation in a real bash shell and the real cat stops at first file which does not exist:

Blockquote
$ cat f.txt nonexistent 1> f1.txt
cat: nonexistent: No such file or directory

My question is related to the fact that the first cat /tmp/foo/baz.md works, but the second one does not cat /tmp/foo/qux.md. I am not sure if the problem is in my code (which wouldn’t be a big surprise) or I am missing something.
I also don’t get when the /tmp/foo/qux.md file is/was created?

Hi @unkn0wn3rr0r, I can barely read Zig, but we can pair-debug.

when the /tmp/foo/qux.md file is/was created?

It should be created at the beginning of cat /tmp/qux/orange nonexistent 1> /tmp/foo/qux.md. The order looks like this:

  1. The shell opens /tmp/foo/qux.md.
  2. cat tries to read /tmp/qux/orange and write to /tmp/foo/qux.md.
  3. cat tries to read nonexistent and write to /tmp/foo/qux.md.

Let me know if you need further assistance.

1 Like

Hi @andy1li and thank you for the reply.

Hmm, your answer actually helped me to understand how ‘cat’ works even better. The problem relies in how I’ve implemented my ‘cat’ function, so I will have to fix that.

Thank you.

1 Like