Am i missing something stupidly evident in the Zig version here ?
viny@nyx ~> zig version
0.17.0-dev.2131+d08989840
viny@nyx ~> ls --color=auto -l /home/viny/.gitconfig
lrwxrwxrwx 1 viny viny 27 27 avril 2025 /home/viny/.gitconfig -> dotfiles/home/dot-gitconfig
viny@nyx ~> ./zlink /home/viny/.gitconfig
/home/viny/.gitconfig is not a symbolic link
viny@nyx ~> ./clink /home/viny/.gitconfig
/home/viny/.gitconfig is a symbolic link
With zlink.zig
const std = @import("std");
pub fn main(init: std.process.Init) !void {
const arena = init.arena.allocator();
const io = init.io;
var it = try init.minimal.args.iterateAllocator(arena);
const program_name = it.next() orelse "zlink";
const arg = it.next() orelse {
std.debug.print("Usage: {s} <path>\n", .{program_name});
std.process.exit(1);
};
const file = try std.Io.Dir.cwd().openFile(io, arg, .{ .mode = .read_only });
const stat = try file.stat(io);
if (stat.kind == .sym_link) {
std.debug.print("{s} is a symbolic link\n", .{arg});
} else {
std.debug.print("{s} is not a symbolic link\n", .{arg});
}
}
and clink.c
#include <stdio.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <path>\n", argv[0]);
return 1;
}
struct stat st;
if (lstat(argv[1], &st) != 0) {
perror("lstat");
return 1;
}
if (S_ISLNK(st.st_mode)) {
printf("%s is a symbolic link\n", argv[1]);
} else {
printf("%s is not a symbolic link\n", argv[1]);
}
return 0;
}
I’m not at the computer right now so I can’t check, but I’m pretty sure it’s because you’re opening the symlink in the Zig version, causing the OS to dereference it. Thereby, you’re stating the target file instead of the link. (Setting .follow_symlinks = false in the open options might help.)
You want const stat = try std.Io.Dir.cwd().statFile(io, arg, .{ .follow_symlinks = false });. In your zig version you were opening a file (following symlinks to even get a handle on one), and then checking the stats. So the equivalent in the C version would be to use fstat, not lstat.