@@ -7,11 +7,17 @@ const Scanner = @import("scanner.zig").Scanner;
77const emitter = @import ("emitter.zig" );
88
99// Platform-aware C stdio bindings.
10- // On Windows, translate-c can't lower stdin/stdout (they're runtime function
11- // calls Zig can't fold at comptime), so we declare the extern functions
12- // manually. On POSIX targets we use a translate-c module wired up in
13- // build.zig and imported as "c". (Zig 0.17 removed @cImport as a language
14- // builtin, so this can no longer be done inline.)
10+ //
11+ // Zig 0.17 removed `@cImport` as a language builtin, so we declare every C
12+ // symbol we need manually here instead of pulling them in from system
13+ // headers. This also keeps cross-compilation working on CI runners where
14+ // `addTranslateC` blows up with `CacheCheckFailed` for cross targets.
15+ //
16+ // On Windows we use UCRT's `_findfirst`/`__acrt_iob_func` family. On POSIX
17+ // (Linux + the BSD-derived Apple platforms) we use stdio + dirent + open(2)
18+ // directly. Stdio FILE* globals have different external symbol names
19+ // across libcs (`stdin`/`stdout`/`stderr` on glibc/musl, `__stdinp` etc.
20+ // on Apple), so we expose them as functions that resolve via `@extern`.
1521const c = if (builtin .os .tag == .windows ) struct {
1622 pub const FILE = opaque {};
1723 pub extern "c" fn __acrt_iob_func (index : c_int ) * FILE ;
@@ -37,7 +43,77 @@ const c = if (builtin.os.tag == .windows) struct {
3743 pub extern "c" fn _findnext (handle : isize , fileinfo : * _finddata_t ) c_int ;
3844 pub extern "c" fn _findclose (handle : isize ) c_int ;
3945 pub extern "c" fn _mkdir (path : [* :0 ]const u8 ) c_int ;
40- } else @import ("c" );
46+ } else struct {
47+ pub const FILE = opaque {};
48+ pub const DIR = opaque {};
49+
50+ /// True when targeting an Apple libc (macOS / iOS / tvOS / watchOS).
51+ /// Used to pick the correct external symbol names + struct layouts.
52+ const apple_libc = builtin .os .tag .isDarwin ();
53+
54+ /// `struct dirent` layout differs between glibc/musl and Apple libc.
55+ /// We only ever read `d_name`, but its offset matters, so the struct
56+ /// has to be accurate up to that field.
57+ pub const dirent = if (apple_libc ) extern struct {
58+ d_ino : u64 ,
59+ d_seekoff : u64 ,
60+ d_reclen : u16 ,
61+ d_namlen : u16 ,
62+ d_type : u8 ,
63+ d_name : [1024 ]u8 ,
64+ } else extern struct {
65+ d_ino : c_ulong ,
66+ d_off : c_long ,
67+ d_reclen : c_ushort ,
68+ d_type : u8 ,
69+ d_name : [256 ]u8 ,
70+ };
71+
72+ pub extern "c" fn fopen (path : [* :0 ]const u8 , mode : [* :0 ]const u8 ) ? * FILE ;
73+ pub extern "c" fn fclose (stream : * FILE ) c_int ;
74+ pub extern "c" fn fread (ptr : [* ]u8 , size : usize , nmemb : usize , stream : * FILE ) usize ;
75+ pub extern "c" fn fwrite (ptr : [* ]const u8 , size : usize , nmemb : usize , stream : * FILE ) usize ;
76+ pub extern "c" fn fseek (stream : * FILE , offset : c_long , whence : c_int ) c_int ;
77+ pub extern "c" fn ftell (stream : * FILE ) c_long ;
78+ pub const SEEK_SET : c_int = 0 ;
79+ pub const SEEK_END : c_int = 2 ;
80+
81+ pub extern "c" fn opendir (name : [* :0 ]const u8 ) ? * DIR ;
82+ pub extern "c" fn readdir (dirp : * DIR ) ? * dirent ;
83+ pub extern "c" fn closedir (dirp : * DIR ) c_int ;
84+
85+ pub extern "c" fn open (path : [* :0 ]const u8 , flags : c_int , ... ) c_int ;
86+ pub extern "c" fn openat (dirfd : c_int , path : [* :0 ]const u8 , flags : c_int , ... ) c_int ;
87+ pub extern "c" fn close (fd : c_int ) c_int ;
88+ pub extern "c" fn read (fd : c_int , buf : [* ]u8 , count : usize ) isize ;
89+ pub extern "c" fn write (fd : c_int , buf : [* ]const u8 , count : usize ) isize ;
90+ pub extern "c" fn lseek (fd : c_int , offset : c_long , whence : c_int ) c_long ;
91+ pub extern "c" fn mkdir (path : [* :0 ]const u8 , mode : c_uint ) c_int ;
92+
93+ // fcntl.h flag values (these *do* differ across platforms — Apple uses
94+ // hex bits, Linux uses octal — so we pick per OS rather than guessing).
95+ pub const O_RDONLY : c_int = 0 ;
96+ pub const O_WRONLY : c_int = 1 ;
97+ pub const O_CREAT : c_int = if (apple_libc ) 0x0200 else 0o100 ;
98+ pub const O_TRUNC : c_int = if (apple_libc ) 0x0400 else 0o1000 ;
99+
100+ // Stdio FILE* globals. The on-disk symbol names differ between Apple
101+ // libc (`__stdinp` etc.) and glibc/musl (`stdin` etc.), so resolve them
102+ // via @extern. Exposed as functions so getStdioPtr's "function-like"
103+ // branch picks them up correctly.
104+ pub fn stdin () * FILE {
105+ const ptr = @extern (** FILE , .{ .name = if (apple_libc ) "__stdinp" else "stdin" });
106+ return ptr .* ;
107+ }
108+ pub fn stdout () * FILE {
109+ const ptr = @extern (** FILE , .{ .name = if (apple_libc ) "__stdoutp" else "stdout" });
110+ return ptr .* ;
111+ }
112+ pub fn stderr () * FILE {
113+ const ptr = @extern (** FILE , .{ .name = if (apple_libc ) "__stderrp" else "stderr" });
114+ return ptr .* ;
115+ }
116+ };
41117
42118fn getStdout () * c.FILE {
43119 if (builtin .os .tag == .windows ) return c .__acrt_iob_func (1 );
0 commit comments