master
 1const std = @import("std");
 2const assert = std.debug.assert;
 3const info = std.log.info;
 4const fatal = std.process.fatal;
 5
 6const Allocator = std.mem.Allocator;
 7
 8var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
 9const gpa = general_purpose_allocator.allocator();
10
11const usage =
12    \\gen_macos_headers_c [dir]
13    \\
14    \\General Options:
15    \\-h, --help                    Print this help and exit
16;
17
18pub fn main() anyerror!void {
19    var arena_allocator = std.heap.ArenaAllocator.init(gpa);
20    defer arena_allocator.deinit();
21    const arena = arena_allocator.allocator();
22
23    const args = try std.process.argsAlloc(arena);
24    if (args.len == 1) fatal("no command or option specified", .{});
25
26    var positionals = std.array_list.Managed([]const u8).init(arena);
27
28    for (args[1..]) |arg| {
29        if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
30            return info(usage, .{});
31        } else try positionals.append(arg);
32    }
33
34    if (positionals.items.len != 1) fatal("expected one positional argument: [dir]", .{});
35
36    var dir = try std.fs.cwd().openDir(positionals.items[0], .{ .follow_symlinks = false });
37    defer dir.close();
38    var paths = std.array_list.Managed([]const u8).init(arena);
39    try findHeaders(arena, dir, "", &paths);
40
41    const SortFn = struct {
42        pub fn lessThan(ctx: void, lhs: []const u8, rhs: []const u8) bool {
43            _ = ctx;
44            return std.mem.lessThan(u8, lhs, rhs);
45        }
46    };
47
48    std.mem.sort([]const u8, paths.items, {}, SortFn.lessThan);
49
50    var buffer: [2000]u8 = undefined;
51    var stdout_writer = std.fs.File.stdout().writerStreaming(&buffer);
52    const w = &stdout_writer.interface;
53    try w.writeAll("#define _XOPEN_SOURCE\n");
54    for (paths.items) |path| {
55        try w.print("#include <{s}>\n", .{path});
56    }
57    try w.writeAll(
58        \\int main(int argc, char **argv) {
59        \\    return 0;
60        \\}
61    );
62    try w.flush();
63}
64
65fn findHeaders(
66    arena: Allocator,
67    dir: std.fs.Dir,
68    prefix: []const u8,
69    paths: *std.array_list.Managed([]const u8),
70) anyerror!void {
71    var it = dir.iterate();
72    while (try it.next()) |entry| {
73        switch (entry.kind) {
74            .directory => {
75                const path = try std.fs.path.join(arena, &.{ prefix, entry.name });
76                var subdir = try dir.openDir(entry.name, .{ .follow_symlinks = false });
77                defer subdir.close();
78                try findHeaders(arena, subdir, path, paths);
79            },
80            .file, .sym_link => {
81                const ext = std.fs.path.extension(entry.name);
82                if (!std.mem.eql(u8, ext, ".h")) continue;
83                const path = try std.fs.path.join(arena, &.{ prefix, entry.name });
84                try paths.append(path);
85            },
86            else => {},
87        }
88    }
89}