Closed
Description
Zig Version
0.14.0
Steps to Reproduce and Observed Behavior
The following Zig program tries to use std.c.readdir
to iterate through a directory.
const std = @import("std");
pub fn main() !void {
const dir = std.c.opendir(".") orelse return error.OpenFailed;
defer _ = std.c.closedir(dir);
while (std.c.readdir(dir)) |entry| {
std.log.info("null terminated={s}", .{std.mem.sliceTo(entry.name[0..], 0)});
std.log.info("full={s}", .{entry.name});
}
}
I would expect the null terminated=X
to print out the name of the file, just like this C program:
#include <dirent.h>
#include <stdio.h>
#include <string.h>
int main() {
DIR *dir = opendir(".");
if (dir == NULL) {
perror("opendir failed");
return 1;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
printf("%s\n", entry->d_name);
}
closedir(dir);
return 0;
}
Here is the result of running these programs:
$ ls
a.out build.zig build.zig.zon main.c src zig-out
$ zig build run
info: null terminated=���v
info: full=���v
�:P��Y+.���&�b� zig-outo��8�n��z�srcwF�
�.main.c�����$�r/a.out-��~��q��6build.zig.zon#��?&>�\w\build.zig�<�xE���|�..���������� .zig-cac
info: null terminated=��z�
�:P��Y+.���&�b�srcwF�
�.main.c�����$�r/a.out-��~��q��6build.zig.zon#��?&>�\w\build.zig�<�xE���|�..���������� .zig-cache
info: null terminated=P��Y+
info: full=P��Y+.���&�b�
�.main.c�����$�r/a.out-��~��q��6build.zig.zon#��?&>�\w\build.zig�<�xE���|�..���������� .zig-cache
info: null terminated=�
�.
info: full=�
�.main.c�����$�r/a.out-��~��q��6build.zig.zon#��?&>�\w\build.zig�<�xE���|�..���������� .zig-cache
info: null terminated=$�r/
info: full=$�r/a.out-��~��q��6build.zig.zon#��?&>�\w\build.zig�<�xE���|�..���������� .zig-cache
info: null terminated=q��6(
info: full=q��6build.zig.zon#��?&>�\w\build.zig�<�xE���|�..���������� .zig-cache
info: null terminated=�\w\
info: full=�\w\build.zig�<�xE���|�..���������� .zig-cache
info: null terminated=��|�
info: full=��|�..���������� .zig-cache
info: null terminated=����
info: full=���� .zig-cache
Expected Behavior
I would expect both the null terminated=X
to print the same value as for the C program:
$ gcc main.c && ./a.out
zig-out
src
.
main.c
a.out
build.zig.zon
build.zig
..
.zig-cache
Here is the dirent
in source:
extern struct {
ino: c_uint,
off: c_uint,
reclen: c_ushort,
type: u8,
name: [256]u8,
}
I think the ino
and the off
should sometimes be u64
and i64
, respectively. For example, this Zig program works fine:
const std = @import("std");
const dirent = extern struct {
ino: u64,
off: i64,
reclen: c_ushort,
type: u8,
name: [256]u8,
};
extern "c" fn readdir(dir: *std.c.DIR) ?*dirent;
pub fn main() !void {
const dir = std.c.opendir(".") orelse return error.OpenFailed;
defer _ = std.c.closedir(dir);
while (readdir(dir)) |entry| {
std.debug.print("{s}\n", .{std.mem.sliceTo(entry.name[0..], 0)});
}
}
$ zig build run
zig-out
src
.
main.c
a.out
build.zig.zon
build.zig
..
.zig-cache
I am willing to contribute to the change, but I am unsure how to detect if we should be using 64-bit ino_t
and off_t
. For example, here is the code from glibc:
struct dirent
{
#ifndef __USE_FILE_OFFSET64
__ino_t d_ino;
__off_t d_off;
#else
__ino64_t d_ino;
__off64_t d_off;
#endif
unsigned short int d_reclen;
unsigned char d_type;
char d_name[256]; /* We must not include limits.h! */
};
Thanks